aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS3
-rw-r--r--default-configs/devices/mips64el-softmmu.mak1
-rw-r--r--docs/devel/clocks.rst51
-rw-r--r--docs/system/target-mips.rst10
-rw-r--r--hw/arm/virt.c3
-rw-r--r--hw/audio/meson.build1
-rw-r--r--hw/audio/via-ac97.c93
-rw-r--r--hw/core/clock.c6
-rw-r--r--hw/hppa/dino.c2
-rw-r--r--hw/i386/xen/xen-hvm.c2
-rw-r--r--hw/intc/loongson_liointc.c36
-rw-r--r--hw/isa/piix3.c2
-rw-r--r--hw/isa/trace-events6
-rw-r--r--hw/isa/vt82c686.c267
-rw-r--r--hw/mips/Kconfig15
-rw-r--r--hw/mips/fuloong2e.c69
-rw-r--r--hw/mips/fw_cfg.c35
-rw-r--r--hw/mips/fw_cfg.h19
-rw-r--r--hw/mips/gt64xxx_pci.c2
-rw-r--r--hw/mips/loongson3_bootp.c151
-rw-r--r--hw/mips/loongson3_bootp.h241
-rw-r--r--hw/mips/loongson3_virt.c638
-rw-r--r--hw/mips/malta.c88
-rw-r--r--hw/mips/meson.build2
-rw-r--r--hw/mips/mipssim.c8
-rw-r--r--hw/pci-host/bonito.c14
-rw-r--r--hw/pci-host/pnv_phb4.c2
-rw-r--r--hw/pci-host/ppce500.c2
-rw-r--r--hw/pci-host/uninorth.c8
-rw-r--r--hw/ppc/ppc4xx_pci.c2
-rw-r--r--hw/sh4/sh_pci.c2
-rw-r--r--include/hw/clock.h53
-rw-r--r--include/hw/intc/loongson_liointc.h22
-rw-r--r--include/hw/isa/vt82c686.h12
-rw-r--r--softmmu/qdev-monitor.c6
-rw-r--r--target/mips/cpu.c4
-rw-r--r--tests/acceptance/boot_linux_console.py21
37 files changed, 1537 insertions, 362 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 42fedf9..da3bd3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1159,6 +1159,9 @@ M: Huacai Chen <chenhuacai@kernel.org>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
S: Maintained
F: hw/intc/loongson_liointc.c
+F: hw/mips/loongson3_bootp.c
+F: hw/mips/loongson3_bootp.h
+F: hw/mips/loongson3_virt.c
Boston
M: Paul Burton <paulburton@kernel.org>
diff --git a/default-configs/devices/mips64el-softmmu.mak b/default-configs/devices/mips64el-softmmu.mak
index 9f8a3ef..26c660a 100644
--- a/default-configs/devices/mips64el-softmmu.mak
+++ b/default-configs/devices/mips64el-softmmu.mak
@@ -3,6 +3,7 @@
include mips-softmmu-common.mak
CONFIG_IDE_VIA=y
CONFIG_FULOONG=y
+CONFIG_LOONGSON3V=y
CONFIG_ATI_VGA=y
CONFIG_RTL8139_PCI=y
CONFIG_JAZZ=y
diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
index e5da28e..2548d84 100644
--- a/docs/devel/clocks.rst
+++ b/docs/devel/clocks.rst
@@ -238,8 +238,17 @@ object during device instance init. For example:
Fetching clock frequency/period
-------------------------------
-To get the current state of a clock, use the functions ``clock_get()``,
-``clock_get_ns()`` or ``clock_get_hz()``.
+To get the current state of a clock, use the functions ``clock_get()``
+or ``clock_get_hz()``.
+
+``clock_get()`` returns the period of the clock in its fully precise
+internal representation, as an unsigned 64-bit integer in units of
+2^-32 nanoseconds. (For many purposes ``clock_ticks_to_ns()`` will
+be more convenient; see the section below on expiry deadlines.)
+
+``clock_get_hz()`` returns the frequency of the clock, rounded to the
+next lowest integer. This implies some inaccuracy due to the rounding,
+so be cautious about using it in calculations.
It is also possible to register a callback on clock frequency changes.
Here is an example:
@@ -254,10 +263,44 @@ Here is an example:
*/
/* do something with the new period */
- fprintf(stdout, "device new period is %" PRIu64 "ns\n",
- clock_get_ns(dev->my_clk_input));
+ fprintf(stdout, "device new period is %" PRIu64 "* 2^-32 ns\n",
+ clock_get(dev->my_clk_input));
}
+If you are only interested in the frequency for displaying it to
+humans (for instance in debugging), use ``clock_display_freq()``,
+which returns a prettified string-representation, e.g. "33.3 MHz".
+The caller must free the string with g_free() after use.
+
+Calculating expiry deadlines
+----------------------------
+
+A commonly required operation for a clock is to calculate how long
+it will take for the clock to tick N times; this can then be used
+to set a timer expiry deadline. Use the function ``clock_ticks_to_ns()``,
+which takes an unsigned 64-bit count of ticks and returns the length
+of time in nanoseconds required for the clock to tick that many times.
+
+It is important not to try to calculate expiry deadlines using a
+shortcut like multiplying a "period of clock in nanoseconds" value
+by the tick count, because clocks can have periods which are not a
+whole number of nanoseconds, and the accumulated error in the
+multiplication can be significant.
+
+For a clock with a very long period and a large number of ticks,
+the result of this function could in theory be too large to fit in
+a 64-bit value. To avoid overflow in this case, ``clock_ticks_to_ns()``
+saturates the result to INT64_MAX (because this is the largest valid
+input to the QEMUTimer APIs). Since INT64_MAX nanoseconds is almost
+300 years, anything with an expiry later than that is in the "will
+never happen" category. Callers of ``clock_ticks_to_ns()`` should
+therefore generally not special-case the possibility of a saturated
+result but just allow the timer to be set to that far-future value.
+(If you are performing further calculations on the returned value
+rather than simply passing it to a QEMUTimer function like
+``timer_mod_ns()`` then you should be careful to avoid overflow
+in those calculations, of course.)
+
Changing a clock period
-----------------------
diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst
index cd2a931..138441b 100644
--- a/docs/system/target-mips.rst
+++ b/docs/system/target-mips.rst
@@ -84,6 +84,16 @@ The Fuloong 2E emulation supports:
- RTL8139D as a network card chipset
+The Loongson-3 virtual platform emulation supports:
+
+- Loongson 3A CPU
+
+- LIOINTC as interrupt controller
+
+- GPEX and virtio as peripheral devices
+
+- Both KVM and TCG supported
+
The mipssim pseudo board emulation provides an environment similar to
what the proprietary MIPS emulator uses for running Linux. It supports:
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 9698591..bf3a717 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1147,7 +1147,8 @@ static void create_pcie_irq_map(const VirtMachineState *vms,
full_irq_map, sizeof(full_irq_map));
qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupt-map-mask",
- 0x1800, 0, 0, /* devfn (PCI_SLOT(3)) */
+ cpu_to_be16(PCI_DEVFN(3, 0)), /* Slot 3 */
+ 0, 0,
0x7 /* PCI irq */);
}
diff --git a/hw/audio/meson.build b/hw/audio/meson.build
index 549e9a0..32c42bd 100644
--- a/hw/audio/meson.build
+++ b/hw/audio/meson.build
@@ -11,4 +11,5 @@ softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-ac97.c'))
softmmu_ss.add(when: 'CONFIG_PCSPK', if_true: files('pcspk.c'))
softmmu_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c'))
softmmu_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c'))
+softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c'))
softmmu_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c'))
diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c
new file mode 100644
index 0000000..6d556f7
--- /dev/null
+++ b/hw/audio/via-ac97.c
@@ -0,0 +1,93 @@
+/*
+ * VIA south bridges sound support
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ */
+
+/*
+ * TODO: This is entirely boiler plate just registering empty PCI devices
+ * with the right ID guests expect, functionality should be added here.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/isa/vt82c686.h"
+#include "hw/pci/pci.h"
+
+static void via_ac97_realize(PCIDevice *pci_dev, Error **errp)
+{
+ pci_set_word(pci_dev->config + PCI_COMMAND,
+ PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY);
+ pci_set_word(pci_dev->config + PCI_STATUS,
+ PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_MEDIUM);
+ pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
+}
+
+static void via_ac97_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = via_ac97_realize;
+ k->vendor_id = PCI_VENDOR_ID_VIA;
+ k->device_id = PCI_DEVICE_ID_VIA_AC97;
+ k->revision = 0x50;
+ k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
+ set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
+ dc->desc = "VIA AC97";
+ /* Reason: Part of a south bridge chip */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo via_ac97_info = {
+ .name = TYPE_VIA_AC97,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIDevice),
+ .class_init = via_ac97_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void via_mc97_realize(PCIDevice *pci_dev, Error **errp)
+{
+ pci_set_word(pci_dev->config + PCI_COMMAND,
+ PCI_COMMAND_INVALIDATE | PCI_COMMAND_VGA_PALETTE);
+ pci_set_word(pci_dev->config + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
+ pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
+}
+
+static void via_mc97_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = via_mc97_realize;
+ k->vendor_id = PCI_VENDOR_ID_VIA;
+ k->device_id = PCI_DEVICE_ID_VIA_MC97;
+ k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
+ k->revision = 0x30;
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+ dc->desc = "VIA MC97";
+ /* Reason: Part of a south bridge chip */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo via_mc97_info = {
+ .name = TYPE_VIA_MC97,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIDevice),
+ .class_init = via_mc97_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void via_ac97_register_types(void)
+{
+ type_register_static(&via_ac97_info);
+ type_register_static(&via_mc97_info);
+}
+
+type_init(via_ac97_register_types)
diff --git a/hw/core/clock.c b/hw/core/clock.c
index 8c6af22..76b5f46 100644
--- a/hw/core/clock.c
+++ b/hw/core/clock.c
@@ -12,6 +12,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
#include "hw/clock.h"
#include "trace.h"
@@ -111,6 +112,11 @@ static void clock_disconnect(Clock *clk)
QLIST_REMOVE(clk, sibling);
}
+char *clock_display_freq(Clock *clk)
+{
+ return freq_to_str(clock_get_hz(clk));
+}
+
static void clock_initfn(Object *obj)
{
Clock *clk = CLOCK(obj);
diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c
index 81053b5..5b82c94 100644
--- a/hw/hppa/dino.c
+++ b/hw/hppa/dino.c
@@ -496,7 +496,7 @@ static void dino_set_irq(void *opaque, int irq, int level)
static int dino_pci_map_irq(PCIDevice *d, int irq_num)
{
- int slot = d->devfn >> 3;
+ int slot = PCI_SLOT(d->devfn);
assert(irq_num >= 0 && irq_num <= 3);
diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c
index 096c46f..68821d9 100644
--- a/hw/i386/xen/xen-hvm.c
+++ b/hw/i386/xen/xen-hvm.c
@@ -140,7 +140,7 @@ typedef struct XenIOState {
int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
{
- return irq_num + ((pci_dev->devfn >> 3) << 2);
+ return irq_num + (PCI_SLOT(pci_dev->devfn) << 2);
}
void xen_piix3_set_irq(void *opaque, int irq_num, int level)
diff --git a/hw/intc/loongson_liointc.c b/hw/intc/loongson_liointc.c
index fbbfb57..f823d48 100644
--- a/hw/intc/loongson_liointc.c
+++ b/hw/intc/loongson_liointc.c
@@ -1,6 +1,7 @@
/*
* QEMU Loongson Local I/O interrupt controler.
*
+ * Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
* Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
*
* This program is free software: you can redistribute it and/or modify
@@ -19,13 +20,11 @@
*/
#include "qemu/osdep.h"
-#include "hw/sysbus.h"
#include "qemu/module.h"
+#include "qemu/log.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
-#include "qom/object.h"
-
-#define D(x)
+#include "hw/intc/loongson_liointc.h"
#define NUM_IRQS 32
@@ -40,13 +39,10 @@
#define R_IEN 0x24
#define R_IEN_SET 0x28
#define R_IEN_CLR 0x2c
-#define R_PERCORE_ISR(x) (0x40 + 0x8 * x)
+#define R_ISR_SIZE 0x8
+#define R_START 0x40
#define R_END 0x64
-#define TYPE_LOONGSON_LIOINTC "loongson.liointc"
-DECLARE_INSTANCE_CHECKER(struct loongson_liointc, LOONGSON_LIOINTC,
- TYPE_LOONGSON_LIOINTC)
-
struct loongson_liointc {
SysBusDevice parent_obj;
@@ -123,14 +119,13 @@ liointc_read(void *opaque, hwaddr addr, unsigned int size)
goto out;
}
- /* Rest is 4 byte */
+ /* Rest are 4 bytes */
if (size != 4 || (addr % 4)) {
goto out;
}
- if (addr >= R_PERCORE_ISR(0) &&
- addr < R_PERCORE_ISR(NUM_CORES)) {
- int core = (addr - R_PERCORE_ISR(0)) / 8;
+ if (addr >= R_START && addr < R_END) {
+ int core = (addr - R_START) / R_ISR_SIZE;
r = p->per_core_isr[core];
goto out;
}
@@ -147,7 +142,8 @@ liointc_read(void *opaque, hwaddr addr, unsigned int size)
}
out:
- D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__, size, addr, r));
+ qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
+ __func__, size, addr, r);
return r;
}
@@ -158,7 +154,8 @@ liointc_write(void *opaque, hwaddr addr,
struct loongson_liointc *p = opaque;
uint32_t value = val64;
- D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__, size, addr, value));
+ qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
+ __func__, size, addr, value);
/* Mapper is 1 byte */
if (size == 1 && addr < R_MAPPER_END) {
@@ -166,14 +163,13 @@ liointc_write(void *opaque, hwaddr addr,
goto out;
}
- /* Rest is 4 byte */
+ /* Rest are 4 bytes */
if (size != 4 || (addr % 4)) {
goto out;
}
- if (addr >= R_PERCORE_ISR(0) &&
- addr < R_PERCORE_ISR(NUM_CORES)) {
- int core = (addr - R_PERCORE_ISR(0)) / 8;
+ if (addr >= R_START && addr < R_END) {
+ int core = (addr - R_START) / R_ISR_SIZE;
p->per_core_isr[core] = value;
goto out;
}
@@ -224,7 +220,7 @@ static void loongson_liointc_init(Object *obj)
}
memory_region_init_io(&p->mmio, obj, &pic_ops, p,
- "loongson.liointc", R_END);
+ TYPE_LOONGSON_LIOINTC, R_END);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
}
diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c
index 587850b..f46ccae 100644
--- a/hw/isa/piix3.c
+++ b/hw/isa/piix3.c
@@ -361,7 +361,7 @@ type_init(piix3_register_types)
static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx)
{
int slot_addend;
- slot_addend = (pci_dev->devfn >> 3) - 1;
+ slot_addend = PCI_SLOT(pci_dev->devfn) - 1;
return (pci_intx + slot_addend) & 3;
}
diff --git a/hw/isa/trace-events b/hw/isa/trace-events
index 3544c62..d267d3e 100644
--- a/hw/isa/trace-events
+++ b/hw/isa/trace-events
@@ -13,3 +13,9 @@ pc87312_io_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x"
# apm.c
apm_io_read(uint8_t addr, uint8_t val) "read addr=0x%x val=0x%02x"
apm_io_write(uint8_t addr, uint8_t val) "write addr=0x%x val=0x%02x"
+
+# vt82c686.c
+via_isa_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
+via_pm_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x"
+via_superio_read(uint8_t addr, uint8_t val) "addr 0x%x val 0x%x"
+via_superio_write(uint8_t addr, uint32_t val) "addr 0x%x val 0x%x"
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c
index b3170c7..a6f5a08 100644
--- a/hw/isa/vt82c686.c
+++ b/hw/isa/vt82c686.c
@@ -12,14 +12,11 @@
#include "qemu/osdep.h"
#include "hw/isa/vt82c686.h"
-#include "hw/i2c/i2c.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "hw/isa/isa.h"
#include "hw/isa/superio.h"
-#include "hw/sysbus.h"
#include "migration/vmstate.h"
-#include "hw/mips/mips.h"
#include "hw/isa/apm.h"
#include "hw/acpi/acpi.h"
#include "hw/i2c/pm_smbus.h"
@@ -27,43 +24,34 @@
#include "qemu/module.h"
#include "qemu/timer.h"
#include "exec/address-spaces.h"
-#include "qom/object.h"
-
-/* #define DEBUG_VT82C686B */
-
-#ifdef DEBUG_VT82C686B
-#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __func__, ##__VA_ARGS__)
-#else
-#define DPRINTF(fmt, ...)
-#endif
+#include "trace.h"
typedef struct SuperIOConfig {
- uint8_t config[0x100];
+ uint8_t regs[0x100];
uint8_t index;
uint8_t data;
} SuperIOConfig;
-struct VT82C686BState {
+struct VT82C686BISAState {
PCIDevice dev;
MemoryRegion superio;
- SuperIOConfig superio_conf;
+ SuperIOConfig superio_cfg;
};
-#define TYPE_VT82C686B_DEVICE "VT82C686B"
-OBJECT_DECLARE_SIMPLE_TYPE(VT82C686BState, VT82C686B_DEVICE)
+OBJECT_DECLARE_SIMPLE_TYPE(VT82C686BISAState, VT82C686B_ISA)
-static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data,
- unsigned size)
+static void superio_cfg_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
{
- SuperIOConfig *superio_conf = opaque;
+ SuperIOConfig *sc = opaque;
- DPRINTF("superio_ioport_writeb address 0x%x val 0x%x\n", addr, data);
- if (addr == 0x3f0) {
- superio_conf->index = data & 0xff;
+ if (addr == 0x3f0) { /* config index register */
+ sc->index = data & 0xff;
} else {
bool can_write = true;
- /* 0x3f1 */
- switch (superio_conf->index) {
+ /* 0x3f1, config data register */
+ trace_via_superio_write(sc->index, data & 0xff);
+ switch (sc->index) {
case 0x00 ... 0xdf:
case 0xe4:
case 0xe5:
@@ -75,39 +63,29 @@ static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data,
case 0xfd ... 0xff:
can_write = false;
break;
- case 0xe7:
- if ((data & 0xff) != 0xfe) {
- DPRINTF("change uart 1 base. unsupported yet\n");
- can_write = false;
- }
- break;
- case 0xe8:
- if ((data & 0xff) != 0xbe) {
- DPRINTF("change uart 2 base. unsupported yet\n");
- can_write = false;
- }
- break;
+ /* case 0xe6 ... 0xe8: Should set base port of parallel and serial */
default:
break;
}
if (can_write) {
- superio_conf->config[superio_conf->index] = data & 0xff;
+ sc->regs[sc->index] = data & 0xff;
}
}
}
-static uint64_t superio_ioport_readb(void *opaque, hwaddr addr, unsigned size)
+static uint64_t superio_cfg_read(void *opaque, hwaddr addr, unsigned size)
{
- SuperIOConfig *superio_conf = opaque;
+ SuperIOConfig *sc = opaque;
+ uint8_t val = sc->regs[sc->index];
- DPRINTF("superio_ioport_readb address 0x%x\n", addr);
- return superio_conf->config[superio_conf->index];
+ trace_via_superio_read(sc->index, val);
+ return val;
}
-static const MemoryRegionOps superio_ops = {
- .read = superio_ioport_readb,
- .write = superio_ioport_writeb,
+static const MemoryRegionOps superio_cfg_ops = {
+ .read = superio_cfg_read,
+ .write = superio_cfg_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.min_access_size = 1,
@@ -117,8 +95,8 @@ static const MemoryRegionOps superio_ops = {
static void vt82c686b_isa_reset(DeviceState *dev)
{
- VT82C686BState *vt82c = VT82C686B_DEVICE(dev);
- uint8_t *pci_conf = vt82c->dev.config;
+ VT82C686BISAState *s = VT82C686B_ISA(dev);
+ uint8_t *pci_conf = s->dev.config;
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
@@ -134,31 +112,27 @@ static void vt82c686b_isa_reset(DeviceState *dev)
pci_conf[0x5f] = 0x04;
pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */
- vt82c->superio_conf.config[0xe0] = 0x3c;
- vt82c->superio_conf.config[0xe2] = 0x03;
- vt82c->superio_conf.config[0xe3] = 0xfc;
- vt82c->superio_conf.config[0xe6] = 0xde;
- vt82c->superio_conf.config[0xe7] = 0xfe;
- vt82c->superio_conf.config[0xe8] = 0xbe;
+ s->superio_cfg.regs[0xe0] = 0x3c; /* Device ID */
+ s->superio_cfg.regs[0xe2] = 0x03; /* Function select */
+ s->superio_cfg.regs[0xe3] = 0xfc; /* Floppy ctrl base addr */
+ s->superio_cfg.regs[0xe6] = 0xde; /* Parallel port base addr */
+ s->superio_cfg.regs[0xe7] = 0xfe; /* Serial port 1 base addr */
+ s->superio_cfg.regs[0xe8] = 0xbe; /* Serial port 2 base addr */
}
/* write config pci function0 registers. PCI-ISA bridge */
-static void vt82c686b_write_config(PCIDevice *d, uint32_t address,
+static void vt82c686b_write_config(PCIDevice *d, uint32_t addr,
uint32_t val, int len)
{
- VT82C686BState *vt686 = VT82C686B_DEVICE(d);
+ VT82C686BISAState *s = VT82C686B_ISA(d);
- DPRINTF("vt82c686b_write_config address 0x%x val 0x%x len 0x%x\n",
- address, val, len);
-
- pci_default_write_config(d, address, val, len);
- if (address == 0x85) { /* enable or disable super IO configure */
- memory_region_set_enabled(&vt686->superio, val & 0x2);
+ trace_via_isa_write(addr, val, len);
+ pci_default_write_config(d, addr, val, len);
+ if (addr == 0x85) { /* enable or disable super IO configure */
+ memory_region_set_enabled(&s->superio, val & 0x2);
}
}
-#define ACPI_DBG_IO_ADDR 0xb044
-
struct VT686PMState {
PCIDevice dev;
MemoryRegion io;
@@ -168,22 +142,7 @@ struct VT686PMState {
uint32_t smb_io_base;
};
-struct VT686AC97State {
- PCIDevice dev;
-};
-
-struct VT686MC97State {
- PCIDevice dev;
-};
-
-#define TYPE_VT82C686B_PM_DEVICE "VT82C686B_PM"
-OBJECT_DECLARE_SIMPLE_TYPE(VT686PMState, VT82C686B_PM_DEVICE)
-
-#define TYPE_VT82C686B_MC97_DEVICE "VT82C686B_MC97"
-OBJECT_DECLARE_SIMPLE_TYPE(VT686MC97State, VT82C686B_MC97_DEVICE)
-
-#define TYPE_VT82C686B_AC97_DEVICE "VT82C686B_AC97"
-OBJECT_DECLARE_SIMPLE_TYPE(VT686AC97State, VT82C686B_AC97_DEVICE)
+OBJECT_DECLARE_SIMPLE_TYPE(VT686PMState, VT82C686B_PM)
static void pm_update_sci(VT686PMState *s)
{
@@ -220,12 +179,10 @@ static void pm_io_space_update(VT686PMState *s)
memory_region_transaction_commit();
}
-static void pm_write_config(PCIDevice *d,
- uint32_t address, uint32_t val, int len)
+static void pm_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len)
{
- DPRINTF("pm_write_config address 0x%x val 0x%x len 0x%x\n",
- address, val, len);
- pci_default_write_config(d, address, val, len);
+ trace_via_pm_write(addr, val, len);
+ pci_default_write_config(d, addr, val, len);
}
static int vmstate_acpi_post_load(void *opaque, int version_id)
@@ -253,104 +210,10 @@ static const VMStateDescription vmstate_acpi = {
}
};
-/*
- * TODO: vt82c686b_ac97_init() and vt82c686b_mc97_init()
- * just register a PCI device now, functionalities will be implemented later.
- */
-
-static void vt82c686b_ac97_realize(PCIDevice *dev, Error **errp)
-{
- VT686AC97State *s = VT82C686B_AC97_DEVICE(dev);
- uint8_t *pci_conf = s->dev.config;
-
- pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
- PCI_COMMAND_PARITY);
- pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST |
- PCI_STATUS_DEVSEL_MEDIUM);
- pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03);
-}
-
-void vt82c686b_ac97_init(PCIBus *bus, int devfn)
-{
- PCIDevice *dev;
-
- dev = pci_new(devfn, TYPE_VT82C686B_AC97_DEVICE);
- pci_realize_and_unref(dev, bus, &error_fatal);
-}
-
-static void via_ac97_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
- k->realize = vt82c686b_ac97_realize;
- k->vendor_id = PCI_VENDOR_ID_VIA;
- k->device_id = PCI_DEVICE_ID_VIA_AC97;
- k->revision = 0x50;
- k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
- set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
- dc->desc = "AC97";
-}
-
-static const TypeInfo via_ac97_info = {
- .name = TYPE_VT82C686B_AC97_DEVICE,
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(VT686AC97State),
- .class_init = via_ac97_class_init,
- .interfaces = (InterfaceInfo[]) {
- { INTERFACE_CONVENTIONAL_PCI_DEVICE },
- { },
- },
-};
-
-static void vt82c686b_mc97_realize(PCIDevice *dev, Error **errp)
-{
- VT686MC97State *s = VT82C686B_MC97_DEVICE(dev);
- uint8_t *pci_conf = s->dev.config;
-
- pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
- PCI_COMMAND_VGA_PALETTE);
- pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
- pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03);
-}
-
-void vt82c686b_mc97_init(PCIBus *bus, int devfn)
-{
- PCIDevice *dev;
-
- dev = pci_new(devfn, TYPE_VT82C686B_MC97_DEVICE);
- pci_realize_and_unref(dev, bus, &error_fatal);
-}
-
-static void via_mc97_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
- k->realize = vt82c686b_mc97_realize;
- k->vendor_id = PCI_VENDOR_ID_VIA;
- k->device_id = PCI_DEVICE_ID_VIA_MC97;
- k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
- k->revision = 0x30;
- set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
- dc->desc = "MC97";
-}
-
-static const TypeInfo via_mc97_info = {
- .name = TYPE_VT82C686B_MC97_DEVICE,
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(VT686MC97State),
- .class_init = via_mc97_class_init,
- .interfaces = (InterfaceInfo[]) {
- { INTERFACE_CONVENTIONAL_PCI_DEVICE },
- { },
- },
-};
-
/* vt82c686 pm init */
static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp)
{
- VT686PMState *s = VT82C686B_PM_DEVICE(dev);
+ VT686PMState *s = VT82C686B_PM(dev);
uint8_t *pci_conf;
pci_conf = s->dev.config;
@@ -380,22 +243,6 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp)
acpi_pm1_cnt_init(&s->ar, &s->io, false, false, 2);
}
-I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
- qemu_irq sci_irq)
-{
- PCIDevice *dev;
- VT686PMState *s;
-
- dev = pci_new(devfn, TYPE_VT82C686B_PM_DEVICE);
- qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base);
-
- s = VT82C686B_PM_DEVICE(dev);
-
- pci_realize_and_unref(dev, bus, &error_fatal);
-
- return s->smb.smbus;
-}
-
static Property via_pm_properties[] = {
DEFINE_PROP_UINT32("smb_io_base", VT686PMState, smb_io_base, 0),
DEFINE_PROP_END_OF_LIST(),
@@ -419,7 +266,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_pm_info = {
- .name = TYPE_VT82C686B_PM_DEVICE,
+ .name = TYPE_VT82C686B_PM,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT686PMState),
.class_init = via_pm_class_init,
@@ -434,7 +281,7 @@ static const VMStateDescription vmstate_via = {
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
- VMSTATE_PCI_DEVICE(dev, VT82C686BState),
+ VMSTATE_PCI_DEVICE(dev, VT82C686BISAState),
VMSTATE_END_OF_LIST()
}
};
@@ -442,7 +289,7 @@ static const VMStateDescription vmstate_via = {
/* init the PCI-to-ISA bridge */
static void vt82c686b_realize(PCIDevice *d, Error **errp)
{
- VT82C686BState *vt82c = VT82C686B_DEVICE(d);
+ VT82C686BISAState *s = VT82C686B_ISA(d);
uint8_t *pci_conf;
ISABus *isa_bus;
uint8_t *wmask;
@@ -464,25 +311,15 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp)
}
}
- memory_region_init_io(&vt82c->superio, OBJECT(d), &superio_ops,
- &vt82c->superio_conf, "superio", 2);
- memory_region_set_enabled(&vt82c->superio, false);
+ memory_region_init_io(&s->superio, OBJECT(d), &superio_cfg_ops,
+ &s->superio_cfg, "superio", 2);
+ memory_region_set_enabled(&s->superio, false);
/*
* The floppy also uses 0x3f0 and 0x3f1.
* But we do not emulate a floppy, so just set it here.
*/
memory_region_add_subregion(isa_bus->address_space_io, 0x3f0,
- &vt82c->superio);
-}
-
-ISABus *vt82c686b_isa_init(PCIBus *bus, int devfn)
-{
- PCIDevice *d;
-
- d = pci_create_simple_multifunction(bus, devfn, true,
- TYPE_VT82C686B_DEVICE);
-
- return ISA_BUS(qdev_get_child_bus(DEVICE(d), "isa.0"));
+ &s->superio);
}
static void via_class_init(ObjectClass *klass, void *data)
@@ -507,9 +344,9 @@ static void via_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_info = {
- .name = TYPE_VT82C686B_DEVICE,
+ .name = TYPE_VT82C686B_ISA,
.parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(VT82C686BState),
+ .instance_size = sizeof(VT82C686BISAState),
.class_init = via_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
@@ -537,8 +374,6 @@ static const TypeInfo via_superio_info = {
static void vt82c686b_register_types(void)
{
- type_register_static(&via_ac97_info);
- type_register_static(&via_mc97_info);
type_register_static(&via_pm_info);
type_register_static(&via_superio_info);
type_register_static(&via_info);
diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig
index 8be7012..aadd436 100644
--- a/hw/mips/Kconfig
+++ b/hw/mips/Kconfig
@@ -32,9 +32,24 @@ 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
config MIPS_BOSTON
bool
+
+config FW_CFG_MIPS
+ bool
diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c
index 45c596f..2980524 100644
--- a/hw/mips/fuloong2e.c
+++ b/hw/mips/fuloong2e.c
@@ -14,8 +14,8 @@
* 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 user manual:
- * http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf
+ * Loongson 2e manuals:
+ * https://github.com/loongson-community/docs/tree/master/2E
*/
#include "qemu/osdep.h"
@@ -47,9 +47,8 @@
#include "sysemu/reset.h"
#include "qemu/error-report.h"
-#define DEBUG_FULOONG2E_INIT
-
-#define ENVP_ADDR 0x80002000l
+#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
@@ -61,14 +60,7 @@
* PMON is not part of qemu and released with BSD license, anyone
* who want to build a pmon binary please first git-clone the source
* from the git repository at:
- * http://www.loongson.cn/support/git/pmon
- * Then follow the "Compile Guide" available at:
- * http://dev.lemote.com/code/pmon
- *
- * Notes:
- * 1, don't use the source at http://dev.lemote.com/http_git/pmon.git
- * 2, use "Bonito2edev" to replace "dir_corresponding_to_your_target_hardware"
- * in the "Compile Guide".
+ * https://github.com/loongson-community/pmon
*/
#define FULOONG_BIOSNAME "pmon_2e.bin"
@@ -100,16 +92,16 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index,
}
table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
- prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
+ 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 int64_t load_kernel(CPUMIPSState *env)
+static uint64_t load_kernel(MIPSCPU *cpu)
{
- int64_t kernel_entry, kernel_high, initrd_size;
+ uint64_t kernel_entry, kernel_high, initrd_size;
int index = 0;
long kernel_size;
ram_addr_t initrd_offset;
@@ -118,8 +110,8 @@ static int64_t load_kernel(CPUMIPSState *env)
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
- (uint64_t *)&kernel_entry, NULL,
- (uint64_t *)&kernel_high, NULL,
+ &kernel_entry, NULL,
+ &kernel_high, NULL,
0, EM_MIPS, 1, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
@@ -167,20 +159,18 @@ static int64_t load_kernel(CPUMIPSState *env)
/* Setup minimum environment variables */
prom_set(prom_buf, index++, "busclock=33000000");
- prom_set(prom_buf, index++, "cpuclock=100000000");
+ 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++, "modetty0=38400n8r");
prom_set(prom_buf, index++, NULL);
- rom_add_blob_fixed("prom", prom_buf, prom_size,
- cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+ 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,
- int64_t kernel_addr)
+ uint64_t kernel_addr)
{
uint32_t *p;
@@ -199,14 +189,14 @@ static void write_bootloader(CPUMIPSState *env, uint8_t *base,
stl_p(p++, 0x3c040000);
/* ori a0, a0, 2 */
stl_p(p++, 0x34840002);
- /* lui a1, high(ENVP_ADDR) */
- stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff));
- /* ori a1, a0, low(ENVP_ADDR) */
- stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff));
- /* lui a2, high(ENVP_ADDR + 8) */
- stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff));
- /* ori a2, a2, low(ENVP_ADDR + 8) */
- stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff));
+ /* lui a1, high(ENVP_VADDR) */
+ stl_p(p++, 0x3c050000 | ((ENVP_VADDR >> 16) & 0xffff));
+ /* ori a1, a0, 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(env->ram_size) */
stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16));
/* ori a3, a3, low(env->ram_size) */
@@ -240,7 +230,9 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc,
ISABus *isa_bus;
PCIDevice *dev;
- isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0));
+ dev = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(slot, 0), true,
+ TYPE_VT82C686B_ISA);
+ isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(dev), "isa.0"));
assert(isa_bus);
*p_isa_bus = isa_bus;
/* Interrupt controller */
@@ -259,11 +251,14 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc,
pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci");
pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci");
- *i2c_bus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(slot, 4), 0xeee1, NULL);
+ dev = pci_new(PCI_DEVFN(slot, 4), TYPE_VT82C686B_PM);
+ qdev_prop_set_uint32(DEVICE(dev), "smb_io_base", 0xeee1);
+ pci_realize_and_unref(dev, pci_bus, &error_fatal);
+ *i2c_bus = I2C_BUS(qdev_get_child_bus(DEVICE(dev), "i2c"));
/* Audio support */
- vt82c686b_ac97_init(pci_bus, PCI_DEVFN(slot, 5));
- vt82c686b_mc97_init(pci_bus, PCI_DEVFN(slot, 6));
+ 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 */
@@ -294,7 +289,7 @@ static void mips_fuloong2e_init(MachineState *machine)
MemoryRegion *bios = g_new(MemoryRegion, 1);
long bios_size;
uint8_t *spd_data;
- int64_t kernel_entry;
+ uint64_t kernel_entry;
PCIDevice *pci_dev;
PCIBus *pci_bus;
ISABus *isa_bus;
@@ -335,7 +330,7 @@ static void mips_fuloong2e_init(MachineState *machine)
loaderparams.kernel_filename = kernel_filename;
loaderparams.kernel_cmdline = kernel_cmdline;
loaderparams.initrd_filename = initrd_filename;
- kernel_entry = load_kernel(env);
+ 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,
diff --git a/hw/mips/fw_cfg.c b/hw/mips/fw_cfg.c
new file mode 100644
index 0000000..67c4a74
--- /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 0000000..e317d5b
--- /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
index e091bc4..588e6f9 100644
--- a/hw/mips/gt64xxx_pci.c
+++ b/hw/mips/gt64xxx_pci.c
@@ -982,7 +982,7 @@ static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num)
{
int slot;
- slot = (pci_dev->devfn >> 3);
+ slot = PCI_SLOT(pci_dev->devfn);
switch (slot) {
/* PIIX4 USB */
diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c
new file mode 100644
index 0000000..f99af22
--- /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 0000000..09f8480
--- /dev/null
+++ b/hw/mips/loongson3_bootp.h
@@ -0,0 +1,241 @@
+/*
+ * 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,
+};
+
+struct MemmapEntry {
+ hwaddr base;
+ hwaddr size;
+};
+
+extern const struct 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 0000000..d4a82fa
--- /dev/null
+++ b/hw/mips/loongson3_virt.c
@@ -0,0 +1,638 @@
+/*
+ * 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 "cpu.h"
+#include "elf.h"
+#include "kvm_mips.h"
+#include "hw/boards.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 "exec/address-spaces.h"
+#include "sysemu/kvm.h"
+#include "sysemu/qtest.h"
+#include "sysemu/reset.h"
+#include "sysemu/runstate.h"
+#include "qemu/log.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 struct 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 struct MemmapEntry loader_memmap[] = {
+ [LOADER_KERNEL] = { 0x00000000, 0x4000000 },
+ [LOADER_INITRD] = { 0x04000000, 0x0 }, /* Variable */
+ [LOADER_CMDLINE] = { 0x0ff00000, 0x100000 },
+};
+
+static const struct 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
index 366f4fd..9afc0b4 100644
--- a/hw/mips/malta.c
+++ b/hw/mips/malta.c
@@ -62,7 +62,8 @@
#include "hw/mips/cps.h"
#include "hw/qdev-clock.h"
-#define ENVP_ADDR 0x80002000l
+#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
@@ -616,8 +617,8 @@ static void network_init(PCIBus *pci_bus)
}
}
-static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr,
- int64_t kernel_entry)
+static void write_bootloader_nanomips(uint8_t *base, uint64_t run_addr,
+ uint64_t kernel_entry)
{
uint16_t *p;
@@ -656,29 +657,29 @@ static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr,
/* li a0,2 */
}
- stw_p(p++, 0xe3a0 | NM_HI1(ENVP_ADDR - 64));
+ stw_p(p++, 0xe3a0 | NM_HI1(ENVP_VADDR - 64));
- stw_p(p++, NM_HI2(ENVP_ADDR - 64));
- /* lui sp,%hi(ENVP_ADDR - 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_ADDR - 64));
- /* ori sp,sp,%lo(ENVP_ADDR - 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_ADDR));
+ stw_p(p++, 0xe0a0 | NM_HI1(ENVP_VADDR));
- stw_p(p++, NM_HI2(ENVP_ADDR));
- /* lui a1,%hi(ENVP_ADDR) */
+ stw_p(p++, NM_HI2(ENVP_VADDR));
+ /* lui a1,%hi(ENVP_VADDR) */
- stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_ADDR));
- /* ori a1,a1,%lo(ENVP_ADDR) */
+ stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_VADDR));
+ /* ori a1,a1,%lo(ENVP_VADDR) */
- stw_p(p++, 0xe0c0 | NM_HI1(ENVP_ADDR + 8));
+ stw_p(p++, 0xe0c0 | NM_HI1(ENVP_VADDR + 8));
- stw_p(p++, NM_HI2(ENVP_ADDR + 8));
- /* lui a2,%hi(ENVP_ADDR + 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_ADDR + 8));
- /* ori a2,a2,%lo(ENVP_ADDR + 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));
@@ -840,8 +841,8 @@ static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr,
* a2 - 32-bit address of the environment variables table
* a3 - RAM size in bytes
*/
-static void write_bootloader(uint8_t *base, int64_t run_addr,
- int64_t kernel_entry)
+static void write_bootloader(uint8_t *base, uint64_t run_addr,
+ uint64_t kernel_entry)
{
uint32_t *p;
@@ -878,18 +879,18 @@ static void write_bootloader(uint8_t *base, int64_t run_addr,
stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */
}
- /* lui sp, high(ENVP_ADDR) */
- stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff));
- /* ori sp, sp, low(ENVP_ADDR) */
- stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff));
- /* lui a1, high(ENVP_ADDR) */
- stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff));
- /* ori a1, a1, low(ENVP_ADDR) */
- stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff));
- /* lui a2, high(ENVP_ADDR + 8) */
- stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff));
- /* ori a2, a2, low(ENVP_ADDR + 8) */
- stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff));
+ /* 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) */
@@ -1003,7 +1004,7 @@ 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;
+ uint32_t table_addr;
if (index >= ENVP_NB_ENTRIES) {
return;
@@ -1014,8 +1015,8 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index,
return;
}
- table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
- prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
+ 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);
@@ -1023,9 +1024,9 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t *prom_buf, int index,
}
/* Kernel */
-static int64_t load_kernel(void)
+static uint64_t load_kernel(void)
{
- int64_t kernel_entry, kernel_high, initrd_size;
+ uint64_t kernel_entry, kernel_high, initrd_size;
long kernel_size;
ram_addr_t initrd_offset;
int big_endian;
@@ -1042,8 +1043,8 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
- (uint64_t *)&kernel_entry, NULL,
- (uint64_t *)&kernel_high, NULL, big_endian, EM_MIPS,
+ &kernel_entry, NULL,
+ &kernel_high, NULL, big_endian, EM_MIPS,
1, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
@@ -1122,8 +1123,7 @@ static int64_t load_kernel(void)
prom_set(prom_buf, prom_index++, "38400n8r");
prom_set(prom_buf, prom_index++, NULL);
- rom_add_blob_fixed("prom", prom_buf, prom_size,
- cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+ rom_add_blob_fixed("prom", prom_buf, prom_size, ENVP_PADDR);
g_free(prom_buf);
return kernel_entry;
@@ -1234,7 +1234,7 @@ void mips_malta_init(MachineState *machine)
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);
- int64_t kernel_entry, bootloader_run_addr;
+ uint64_t kernel_entry, bootloader_run_addr;
PCIBus *pci_bus;
ISABus *isa_bus;
qemu_irq cbus_irq, i8259_irq;
@@ -1302,9 +1302,9 @@ void mips_malta_init(MachineState *machine)
/* For KVM we reserve 1MB of RAM for running bootloader */
if (kvm_enabled()) {
ram_low_size -= 0x100000;
- bootloader_run_addr = 0x40000000 + ram_low_size;
+ bootloader_run_addr = cpu_mips_kvm_um_phys_to_kseg0(NULL, ram_low_size);
} else {
- bootloader_run_addr = 0xbfc00000;
+ bootloader_run_addr = cpu_mips_phys_to_kseg0(NULL, RESET_ADDRESS);
}
/* Write a small bootloader to the flash location. */
diff --git a/hw/mips/meson.build b/hw/mips/meson.build
index 77b4d8f..ee19cc2 100644
--- a/hw/mips/meson.build
+++ b/hw/mips/meson.build
@@ -1,6 +1,8 @@
mips_ss = ss.source_set()
mips_ss.add(files('mips_int.c'))
+mips_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c'))
mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
+mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c', 'loongson3_virt.c'))
mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c'))
mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c'))
diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c
index f2e6273..f5d0da0 100644
--- a/hw/mips/mipssim.c
+++ b/hw/mips/mipssim.c
@@ -60,9 +60,9 @@ typedef struct ResetData {
uint64_t vector;
} ResetData;
-static int64_t load_kernel(void)
+static uint64_t load_kernel(void)
{
- int64_t entry, kernel_high, initrd_size;
+ uint64_t entry, kernel_high, initrd_size;
long kernel_size;
ram_addr_t initrd_offset;
int big_endian;
@@ -75,8 +75,8 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
- (uint64_t *)&entry, NULL,
- (uint64_t *)&kernel_high, NULL, big_endian,
+ &entry, NULL,
+ &kernel_high, NULL, big_endian,
EM_MIPS, 1, 0);
if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c
index a99eced..2a2db7c 100644
--- a/hw/pci-host/bonito.c
+++ b/hw/pci-host/bonito.c
@@ -196,8 +196,7 @@ FIELD(BONGENCFG, PCIQUEUE, 12, 1)
#define PCI_IDSEL_VIA686B (1 << PCI_IDSEL_VIA686B_BIT)
#define PCI_ADDR(busno , devno , funno , regno) \
- ((((busno) << 16) & 0xff0000) + (((devno) << 11) & 0xf800) + \
- (((funno) << 8) & 0x700) + (regno))
+ ((PCI_BUILD_BDF(busno, PCI_DEVFN(devno , funno)) << 8) + (regno))
typedef struct BonitoState BonitoState;
@@ -469,8 +468,8 @@ static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr)
regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET;
if (idsel == 0) {
- error_report("error in bonito pci config address " TARGET_FMT_plx
- ",pcimap_cfg=%x", addr, s->regs[BONITO_PCIMAP_CFG]);
+ error_report("error in bonito pci config address 0x" TARGET_FMT_plx
+ ",pcimap_cfg=0x%x", addr, s->regs[BONITO_PCIMAP_CFG]);
exit(1);
}
pciaddr = PCI_ADDR(pci_bus_num(phb->bus), devno, funno, regno);
@@ -571,7 +570,7 @@ static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num)
{
int slot;
- slot = (pci_dev->devfn >> 3);
+ slot = PCI_SLOT(pci_dev->devfn);
switch (slot) {
case 5: /* FULOONG2E_VIA_SLOT, SouthBridge, IDE, USB, ACPI, AC97, MC97 */
@@ -632,7 +631,7 @@ static void bonito_pcihost_realize(DeviceState *dev, Error **errp)
phb->bus = pci_register_root_bus(dev, "pci",
pci_bonito_set_irq, pci_bonito_map_irq,
dev, &bs->pci_mem, get_system_io(),
- 0x28, 32, TYPE_PCI_BUS);
+ PCI_DEVFN(5, 0), 32, TYPE_PCI_BUS);
for (size_t i = 0; i < 3; i++) {
char *name = g_strdup_printf("pci.lomem%zu", i);
@@ -729,7 +728,8 @@ static void bonito_realize(PCIDevice *dev, Error **errp)
pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x0000);
pci_set_byte(dev->config + PCI_INTERRUPT_LINE, 0x00);
- pci_set_byte(dev->config + PCI_INTERRUPT_PIN, 0x01);
+ pci_config_set_interrupt_pin(dev->config, 0x01); /* interrupt pin A */
+
pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c);
pci_set_byte(dev->config + PCI_MAX_LAT, 0x00);
diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c
index 03daf40..6328e98 100644
--- a/hw/pci-host/pnv_phb4.c
+++ b/hw/pci-host/pnv_phb4.c
@@ -889,7 +889,7 @@ static bool pnv_phb4_resolve_pe(PnvPhb4DMASpace *ds)
/* Read RTE */
bus_num = pci_bus_num(ds->bus);
addr = rtt & PHB_RTT_BASE_ADDRESS_MASK;
- addr += 2 * ((bus_num << 8) | ds->devfn);
+ addr += 2 * PCI_BUILD_BDF(bus_num, ds->devfn);
if (dma_memory_read(&address_space_memory, addr, &rte, sizeof(rte))) {
phb_error(ds->phb, "Failed to read RTT entry at 0x%"PRIx64, addr);
/* Set error bits ? fence ? ... */
diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c
index 9517aab..5ad1424 100644
--- a/hw/pci-host/ppce500.c
+++ b/hw/pci-host/ppce500.c
@@ -342,7 +342,7 @@ static const MemoryRegionOps e500_pci_reg_ops = {
static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int pin)
{
- int devno = pci_dev->devfn >> 3;
+ int devno = PCI_SLOT(pci_dev->devfn);
int ret;
ret = ppce500_pci_map_irq_slot(devno, pin);
diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c
index 0c0a9ec..d25b62d 100644
--- a/hw/pci-host/uninorth.c
+++ b/hw/pci-host/uninorth.c
@@ -63,15 +63,13 @@ static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
if (slot == 32) {
slot = -1; /* XXX: should this be 0? */
}
- func = (reg >> 8) & 7;
+ func = PCI_FUNC(reg >> 8);
/* ... and then convert them to x86 format */
/* config pointer */
retval = (reg & (0xff - 7)) | (addr & 7);
- /* slot */
- retval |= slot << 11;
- /* fn */
- retval |= func << 8;
+ /* slot, fn */
+ retval |= PCI_DEVFN(slot, func) << 8;
}
trace_unin_get_config_reg(reg, addr, retval);
diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c
index 28724c0..e8789f6 100644
--- a/hw/ppc/ppc4xx_pci.c
+++ b/hw/ppc/ppc4xx_pci.c
@@ -243,7 +243,7 @@ static void ppc4xx_pci_reset(void *opaque)
* may need further refactoring for other boards. */
static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
{
- int slot = pci_dev->devfn >> 3;
+ int slot = PCI_SLOT(pci_dev->devfn);
trace_ppc4xx_pci_map_irq(pci_dev->devfn, irq_num, slot);
diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c
index 73d2d0b..734892f 100644
--- a/hw/sh4/sh_pci.c
+++ b/hw/sh4/sh_pci.c
@@ -109,7 +109,7 @@ static const MemoryRegionOps sh_pci_reg_ops = {
static int sh_pci_map_irq(PCIDevice *d, int irq_num)
{
- return (d->devfn >> 3);
+ return PCI_SLOT(d->devfn);
}
static void sh_pci_set_irq(void *opaque, int irq_num, int level)
diff --git a/include/hw/clock.h b/include/hw/clock.h
index 81bcf3e..6382f34 100644
--- a/include/hw/clock.h
+++ b/include/hw/clock.h
@@ -16,6 +16,8 @@
#include "qom/object.h"
#include "qemu/queue.h"
+#include "qemu/host-utils.h"
+#include "qemu/bitops.h"
#define TYPE_CLOCK "clock"
OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
@@ -38,7 +40,6 @@ typedef void ClockCallback(void *opaque);
* macro helpers to convert to hertz / nanosecond
*/
#define CLOCK_PERIOD_FROM_NS(ns) ((ns) * (CLOCK_PERIOD_1SEC / 1000000000llu))
-#define CLOCK_PERIOD_TO_NS(per) ((per) / (CLOCK_PERIOD_1SEC / 1000000000llu))
#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u)
#define CLOCK_PERIOD_TO_HZ(per) (((per) != 0) ? CLOCK_PERIOD_1SEC / (per) : 0u)
@@ -213,9 +214,43 @@ static inline unsigned clock_get_hz(Clock *clk)
return CLOCK_PERIOD_TO_HZ(clock_get(clk));
}
-static inline unsigned clock_get_ns(Clock *clk)
+/**
+ * clock_ticks_to_ns:
+ * @clk: the clock to query
+ * @ticks: number of ticks
+ *
+ * Returns the length of time in nanoseconds for this clock
+ * to tick @ticks times. Because a clock can have a period
+ * which is not a whole number of nanoseconds, it is important
+ * to use this function when calculating things like timer
+ * expiry deadlines, rather than attempting to obtain a "period
+ * in nanoseconds" value and then multiplying that by a number
+ * of ticks.
+ *
+ * The result could in theory be too large to fit in a 64-bit
+ * value if the number of ticks and the clock period are both
+ * large; to avoid overflow the result will be saturated to INT64_MAX
+ * (because this is the largest valid input to the QEMUTimer APIs).
+ * Since INT64_MAX nanoseconds is almost 300 years, anything with
+ * an expiry later than that is in the "will never happen" category
+ * and callers can reasonably not special-case the saturated result.
+ */
+static inline uint64_t clock_ticks_to_ns(const Clock *clk, uint64_t ticks)
{
- return CLOCK_PERIOD_TO_NS(clock_get(clk));
+ uint64_t ns_low, ns_high;
+
+ /*
+ * clk->period is the period in units of 2^-32 ns, so
+ * (clk->period * ticks) is the required length of time in those
+ * units, and we can convert to nanoseconds by multiplying by
+ * 2^32, which is the same as shifting the 128-bit multiplication
+ * result right by 32.
+ */
+ mulu64(&ns_low, &ns_high, clk->period, ticks);
+ if (ns_high & MAKE_64BIT_MASK(31, 33)) {
+ return INT64_MAX;
+ }
+ return ns_low >> 32 | ns_high << 32;
}
/**
@@ -229,4 +264,16 @@ static inline bool clock_is_enabled(const Clock *clk)
return clock_get(clk) != 0;
}
+/**
+ * clock_display_freq: return human-readable representation of clock frequency
+ * @clk: clock
+ *
+ * Return a string which has a human-readable representation of the
+ * clock's frequency, e.g. "33.3 MHz". This is intended for debug
+ * and display purposes.
+ *
+ * The caller is responsible for freeing the string with g_free().
+ */
+char *clock_display_freq(Clock *clk);
+
#endif /* QEMU_HW_CLOCK_H */
diff --git a/include/hw/intc/loongson_liointc.h b/include/hw/intc/loongson_liointc.h
new file mode 100644
index 0000000..848e65e
--- /dev/null
+++ b/include/hw/intc/loongson_liointc.h
@@ -0,0 +1,22 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
+ * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ */
+
+#ifndef LOONGSON_LIOINTC_H
+#define LOONGSON_LIOINTC_H
+
+#include "qemu/units.h"
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_LOONGSON_LIOINTC "loongson.liointc"
+DECLARE_INSTANCE_CHECKER(struct loongson_liointc, LOONGSON_LIOINTC,
+ TYPE_LOONGSON_LIOINTC)
+
+#endif /* LOONGSON_LIOINTC_H */
diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h
index f23f45d..5b0a1ff 100644
--- a/include/hw/isa/vt82c686.h
+++ b/include/hw/isa/vt82c686.h
@@ -1,14 +1,10 @@
#ifndef HW_VT82C686_H
#define HW_VT82C686_H
-
+#define TYPE_VT82C686B_ISA "vt82c686b-isa"
#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio"
-
-/* vt82c686.c */
-ISABus *vt82c686b_isa_init(PCIBus * bus, int devfn);
-void vt82c686b_ac97_init(PCIBus *bus, int devfn);
-void vt82c686b_mc97_init(PCIBus *bus, int devfn);
-I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
- qemu_irq sci_irq);
+#define TYPE_VT82C686B_PM "vt82c686b-pm"
+#define TYPE_VIA_AC97 "via-ac97"
+#define TYPE_VIA_MC97 "via-mc97"
#endif
diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c
index 2c57e36..8dc656b 100644
--- a/softmmu/qdev-monitor.c
+++ b/softmmu/qdev-monitor.c
@@ -736,11 +736,11 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
}
}
QLIST_FOREACH(ncl, &dev->clocks, node) {
- qdev_printf("clock-%s%s \"%s\" freq_hz=%e\n",
+ g_autofree char *freq_str = clock_display_freq(ncl->clock);
+ qdev_printf("clock-%s%s \"%s\" freq_hz=%s\n",
ncl->output ? "out" : "in",
ncl->alias ? " (alias)" : "",
- ncl->name,
- CLOCK_PERIOD_TO_HZ(1.0 * clock_get(ncl->clock)));
+ ncl->name, freq_str);
}
class = object_get_class(OBJECT(dev));
do {
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index b2cd69f..2283214 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -380,8 +380,8 @@ static void mips_cp0_period_set(MIPSCPU *cpu)
{
CPUMIPSState *env = &cpu->env;
- env->cp0_count_ns = cpu->cp0_count_rate
- * clock_get_ns(MIPS_CPU(cpu)->clock);
+ env->cp0_count_ns = clock_ticks_to_ns(MIPS_CPU(cpu)->clock,
+ cpu->cp0_count_rate);
assert(env->cp0_count_ns);
}
diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py
index cc6ec0f..fb41bb7 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -170,6 +170,27 @@ class BootLinuxConsole(LinuxKernelTest):
console_pattern = 'Kernel command line: %s' % kernel_command_line
self.wait_for_console_pattern(console_pattern)
+ def test_mips64el_fuloong2e(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:fuloong2e
+ :avocado: tags=endian:little
+ """
+ deb_url = ('http://archive.debian.org/debian/pool/main/l/linux/'
+ 'linux-image-3.16.0-6-loongson-2e_3.16.56-1+deb8u1_mipsel.deb')
+ deb_hash = 'd04d446045deecf7b755ef576551de0c4184dd44'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-3.16.0-6-loongson-2e')
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
def test_mips_malta_cpio(self):
"""
:avocado: tags=arch:mips