diff options
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 |