diff options
80 files changed, 2332 insertions, 809 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 478bea667c..4be087b88e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1161,6 +1161,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> @@ -1682,6 +1685,8 @@ F: hw/ppc/ppc4*.c F: hw/i2c/ppc4xx_i2c.c F: include/hw/ppc/ppc4xx.h F: include/hw/i2c/ppc4xx_i2c.h +F: hw/intc/ppc-uic.c +F: include/hw/intc/ppc-uic.h Character devices M: Marc-André Lureau <marcandre.lureau@redhat.com> diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 4eda24e63a..2e36d6eb0c 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -28,6 +28,17 @@ DEF_HELPER_FLAGS_1(lookup_tb_ptr, TCG_CALL_NO_WG_SE, ptr, env) DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env) +#ifndef IN_HELPER_PROTO +/* + * Pass calls to memset directly to libc, without a thunk in qemu. + * Do not re-declare memset, especially since we fudge the type here; + * we assume sizeof(void *) == sizeof(size_t), which is true for + * all supported hosts. + */ +#define helper_memset memset +DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr) +#endif /* IN_HELPER_PROTO */ + #ifdef CONFIG_SOFTMMU DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG, diff --git a/default-configs/devices/mips64el-softmmu.mak b/default-configs/devices/mips64el-softmmu.mak index 9f8a3ef156..26c660a05c 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 e5da28e211..2548d84232 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/devel/tracing.txt b/docs/devel/tracing.txt index d2160655b4..dba43fc7a4 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.txt @@ -318,7 +318,8 @@ probes: --target-type system \ --target-name x86_64 \ --group=all \ - trace-events-all >qemu.stp + trace-events-all \ + qemu.stp To facilitate simple usage of systemtap where there merely needs to be printf logging of certain probes, a helper script "qemu-trace-stap" is provided. diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst index cd2a931edf..138441bdec 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 96985917d3..bf3a717111 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 549e9a0396..32c42bdebe 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 0000000000..6d556f74fc --- /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 8c6af223e7..76b5f468b6 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 81053b5fb6..5b82c9440d 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 096c46fef1..68821d90f5 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/Kconfig b/hw/intc/Kconfig index d07954086a..c18d11142a 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -30,23 +30,11 @@ config ARM_GIC_KVM default y depends on ARM_GIC && KVM -config OPENPIC_KVM - bool - default y - depends on OPENPIC && KVM - config XICS bool - depends on POWERNV || PSERIES - -config XICS_SPAPR - bool - select XICS -config XICS_KVM +config XIVE bool - default y - depends on XICS && KVM config ALLWINNER_A10_PIC bool @@ -62,6 +50,9 @@ config S390_FLIC_KVM config OMPIC bool +config PPC_UIC + bool + config RX_ICU bool diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index ffec4a07ee..984334fa7b 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -51,6 +51,8 @@ #define FORCE_OFFSET 0x80 #define EXTENDED_OFFSET 0xC0 +#define MAX_PILS 16 + OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP) typedef struct IRQMPState IRQMPState; @@ -126,7 +128,7 @@ void grlib_irqmp_ack(DeviceState *dev, int intno) grlib_irqmp_ack_mask(state, mask); } -void grlib_irqmp_set_irq(void *opaque, int irq, int level) +static void grlib_irqmp_set_irq(void *opaque, int irq, int level) { IRQMP *irqmp = GRLIB_IRQMP(opaque); IRQMPState *s; @@ -328,6 +330,7 @@ static void grlib_irqmp_init(Object *obj) IRQMP *irqmp = GRLIB_IRQMP(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); + qdev_init_gpio_in(DEVICE(obj), grlib_irqmp_set_irq, MAX_PILS); qdev_init_gpio_out_named(DEVICE(obj), &irqmp->irq, "grlib-irq", 1); memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp, "irqmp", IRQMP_REG_SIZE); diff --git a/hw/intc/loongson_liointc.c b/hw/intc/loongson_liointc.c index fbbfb57ee9..f823d484e0 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/intc/meson.build b/hw/intc/meson.build index 7c3e9daf58..53cba11569 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -39,8 +39,10 @@ specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_lioint specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gic.c')) specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) specific_ss.add(when: 'CONFIG_OMPIC', if_true: files('ompic.c')) -specific_ss.add(when: 'CONFIG_OPENPIC_KVM', if_true: files('openpic_kvm.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_OPENPIC'], + if_true: files('openpic_kvm.c')) specific_ss.add(when: 'CONFIG_POWERNV', if_true: files('xics_pnv.c', 'pnv_xive.c')) +specific_ss.add(when: 'CONFIG_PPC_UIC', if_true: files('ppc-uic.c')) specific_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c')) specific_ss.add(when: 'CONFIG_RX_ICU', if_true: files('rx_icu.c')) specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c')) @@ -49,8 +51,9 @@ specific_ss.add(when: 'CONFIG_SH4', if_true: files('sh_intc.c')) specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c')) specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c')) specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c')) -specific_ss.add(when: 'CONFIG_XICS_KVM', if_true: files('xics_kvm.c')) -specific_ss.add(when: 'CONFIG_XICS_SPAPR', if_true: files('xics_spapr.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'], + if_true: files('xics_kvm.c')) +specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xive.c')) specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) -specific_ss.add(when: 'CONFIG_XIVE_KVM', if_true: files('spapr_xive_kvm.c')) -specific_ss.add(when: 'CONFIG_XIVE_SPAPR', if_true: files('spapr_xive.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], + if_true: files('spapr_xive_kvm.c')) diff --git a/hw/intc/ppc-uic.c b/hw/intc/ppc-uic.c new file mode 100644 index 0000000000..b21951eea8 --- /dev/null +++ b/hw/intc/ppc-uic.c @@ -0,0 +1,321 @@ +/* + * "Universal" Interrupt Controller for PowerPPC 4xx embedded processors + * + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "include/hw/intc/ppc-uic.h" +#include "hw/irq.h" +#include "cpu.h" +#include "hw/ppc/ppc.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qapi/error.h" + +enum { + DCR_UICSR = 0x000, + DCR_UICSRS = 0x001, + DCR_UICER = 0x002, + DCR_UICCR = 0x003, + DCR_UICPR = 0x004, + DCR_UICTR = 0x005, + DCR_UICMSR = 0x006, + DCR_UICVR = 0x007, + DCR_UICVCR = 0x008, + DCR_UICMAX = 0x009, +}; + +/*#define DEBUG_UIC*/ + +#ifdef DEBUG_UIC +# define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) +#else +# define LOG_UIC(...) do { } while (0) +#endif + +static void ppcuic_trigger_irq(PPCUIC *uic) +{ + uint32_t ir, cr; + int start, end, inc, i; + + /* Trigger interrupt if any is pending */ + ir = uic->uicsr & uic->uicer & (~uic->uiccr); + cr = uic->uicsr & uic->uicer & uic->uiccr; + LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 + " uiccr %08" PRIx32 "\n" + " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", + __func__, uic->uicsr, uic->uicer, uic->uiccr, + uic->uicsr & uic->uicer, ir, cr); + if (ir != 0x0000000) { + LOG_UIC("Raise UIC interrupt\n"); + qemu_irq_raise(uic->output_int); + } else { + LOG_UIC("Lower UIC interrupt\n"); + qemu_irq_lower(uic->output_int); + } + /* Trigger critical interrupt if any is pending and update vector */ + if (cr != 0x0000000) { + qemu_irq_raise(uic->output_cint); + if (uic->use_vectors) { + /* Compute critical IRQ vector */ + if (uic->uicvcr & 1) { + start = 31; + end = 0; + inc = -1; + } else { + start = 0; + end = 31; + inc = 1; + } + uic->uicvr = uic->uicvcr & 0xFFFFFFFC; + for (i = start; i <= end; i += inc) { + if (cr & (1 << i)) { + uic->uicvr += (i - start) * 512 * inc; + break; + } + } + } + LOG_UIC("Raise UIC critical interrupt - " + "vector %08" PRIx32 "\n", uic->uicvr); + } else { + LOG_UIC("Lower UIC critical interrupt\n"); + qemu_irq_lower(uic->output_cint); + uic->uicvr = 0x00000000; + } +} + +static void ppcuic_set_irq(void *opaque, int irq_num, int level) +{ + PPCUIC *uic; + uint32_t mask, sr; + + uic = opaque; + mask = 1U << (31 - irq_num); + LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 + " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", + __func__, irq_num, level, + uic->uicsr, mask, uic->uicsr & mask, level << irq_num); + if (irq_num < 0 || irq_num > 31) { + return; + } + sr = uic->uicsr; + + /* Update status register */ + if (uic->uictr & mask) { + /* Edge sensitive interrupt */ + if (level == 1) { + uic->uicsr |= mask; + } + } else { + /* Level sensitive interrupt */ + if (level == 1) { + uic->uicsr |= mask; + uic->level |= mask; + } else { + uic->uicsr &= ~mask; + uic->level &= ~mask; + } + } + LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " + "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); + if (sr != uic->uicsr) { + ppcuic_trigger_irq(uic); + } +} + +static uint32_t dcr_read_uic(void *opaque, int dcrn) +{ + PPCUIC *uic; + uint32_t ret; + + uic = opaque; + dcrn -= uic->dcr_base; + switch (dcrn) { + case DCR_UICSR: + case DCR_UICSRS: + ret = uic->uicsr; + break; + case DCR_UICER: + ret = uic->uicer; + break; + case DCR_UICCR: + ret = uic->uiccr; + break; + case DCR_UICPR: + ret = uic->uicpr; + break; + case DCR_UICTR: + ret = uic->uictr; + break; + case DCR_UICMSR: + ret = uic->uicsr & uic->uicer; + break; + case DCR_UICVR: + if (!uic->use_vectors) { + goto no_read; + } + ret = uic->uicvr; + break; + case DCR_UICVCR: + if (!uic->use_vectors) { + goto no_read; + } + ret = uic->uicvcr; + break; + default: + no_read: + ret = 0x00000000; + break; + } + + return ret; +} + +static void dcr_write_uic(void *opaque, int dcrn, uint32_t val) +{ + PPCUIC *uic; + + uic = opaque; + dcrn -= uic->dcr_base; + LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); + switch (dcrn) { + case DCR_UICSR: + uic->uicsr &= ~val; + uic->uicsr |= uic->level; + ppcuic_trigger_irq(uic); + break; + case DCR_UICSRS: + uic->uicsr |= val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICER: + uic->uicer = val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICCR: + uic->uiccr = val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICPR: + uic->uicpr = val; + break; + case DCR_UICTR: + uic->uictr = val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICMSR: + break; + case DCR_UICVR: + break; + case DCR_UICVCR: + uic->uicvcr = val & 0xFFFFFFFD; + ppcuic_trigger_irq(uic); + break; + } +} + +static void ppc_uic_reset(DeviceState *dev) +{ + PPCUIC *uic = PPC_UIC(dev); + + uic->uiccr = 0x00000000; + uic->uicer = 0x00000000; + uic->uicpr = 0x00000000; + uic->uicsr = 0x00000000; + uic->uictr = 0x00000000; + if (uic->use_vectors) { + uic->uicvcr = 0x00000000; + uic->uicvr = 0x0000000; + } +} + +static void ppc_uic_realize(DeviceState *dev, Error **errp) +{ + PPCUIC *uic = PPC_UIC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + PowerPCCPU *cpu; + int i; + + if (!uic->cpu) { + /* This is a programming error in the code using this device */ + error_setg(errp, "ppc-uic 'cpu' link property was not set"); + return; + } + + cpu = POWERPC_CPU(uic->cpu); + for (i = 0; i < DCR_UICMAX; i++) { + ppc_dcr_register(&cpu->env, uic->dcr_base + i, uic, + &dcr_read_uic, &dcr_write_uic); + } + + sysbus_init_irq(sbd, &uic->output_int); + sysbus_init_irq(sbd, &uic->output_cint); + qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ); +} + +static Property ppc_uic_properties[] = { + DEFINE_PROP_LINK("cpu", PPCUIC, cpu, TYPE_CPU, CPUState *), + DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0x30), + DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription ppc_uic_vmstate = { + .name = "ppc-uic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, PPCUIC), + VMSTATE_UINT32(uicsr, PPCUIC), + VMSTATE_UINT32(uicer, PPCUIC), + VMSTATE_UINT32(uiccr, PPCUIC), + VMSTATE_UINT32(uicpr, PPCUIC), + VMSTATE_UINT32(uictr, PPCUIC), + VMSTATE_UINT32(uicvcr, PPCUIC), + VMSTATE_UINT32(uicvr, PPCUIC), + VMSTATE_END_OF_LIST() + }, +}; + +static void ppc_uic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = ppc_uic_reset; + dc->realize = ppc_uic_realize; + dc->vmsd = &ppc_uic_vmstate; + device_class_set_props(dc, ppc_uic_properties); +} + +static const TypeInfo ppc_uic_info = { + .name = TYPE_PPC_UIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PPCUIC), + .class_init = ppc_uic_class_init, +}; + +static void ppc_uic_register_types(void) +{ + type_register_static(&ppc_uic_info); +} + +type_init(ppc_uic_register_types); diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index caedd312d7..801bc19341 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -156,7 +156,7 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, #define spapr_xive_in_kernel(xive) \ (kvm_irqchip_in_kernel() && (xive)->fd != -1) -void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) +static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) { XiveSource *xsrc = &xive->source; int i; diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c index 587850b888..f46ccae25c 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 3544c6213c..d267d3e652 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 b3170c70c3..a6f5a0843d 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 8be70122f4..aadd436bf4 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 45c596f4fe..29805242ca 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 0000000000..67c4a74f4b --- /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 0000000000..e317d5b9a3 --- /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 e091bc4ed5..588e6f9930 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 0000000000..f99af22932 --- /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 0000000000..09f8480abf --- /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 0000000000..d4a82fa536 --- /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 366f4fdfcd..9afc0b427b 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 77b4d8f365..ee19cc204d 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 f2e6273525..f5d0da05aa 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/Kconfig b/hw/pci-host/Kconfig index 036a61877a..eb03f0489d 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -60,3 +60,8 @@ config PCI_BONITO select PCI select UNIMP bool + +config PCI_POWERNV + select PCI_EXPRESS + select MSI_NONBROKEN + select PCIE_PORT diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index a99eced065..2a2db7cea6 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/meson.build b/hw/pci-host/meson.build index e6d1b89684..da9d1a9964 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -23,7 +23,7 @@ pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c')) softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) -specific_ss.add(when: 'CONFIG_POWERNV', if_true: files( +specific_ss.add(when: 'CONFIG_PCI_POWERNV', if_true: files( 'pnv_phb3.c', 'pnv_phb3_msi.c', 'pnv_phb3_pbcq.c', diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 03daf40a23..6328e985f8 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 9517aab913..5ad1424b31 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 0c0a9ecee1..d25b62d6a5 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/Kconfig b/hw/ppc/Kconfig index dd86e664d2..7e267d94a1 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -7,8 +7,8 @@ config PSERIES select PCI select SPAPR_VSCSI select VFIO if LINUX # needed by spapr_pci_vfio.c - select XICS_SPAPR - select XIVE_SPAPR + select XICS + select XIVE select MSI_NONBROKEN select FDT_PPC select CHRP_NVRAM @@ -29,15 +29,13 @@ config POWERNV select XICS select XIVE select FDT_PPC - select PCI_EXPRESS - select MSI_NONBROKEN + select PCI_POWERNV config PPC405 bool select M48T59 select PFLASH_CFI02 select PPC4XX - select SERIAL config PPC440 bool @@ -46,13 +44,14 @@ config PPC440 imply E1000_PCI select PCI_EXPRESS select PPC4XX - select SERIAL select FDT_PPC config PPC4XX bool select BITBANG_I2C select PCI + select PPC_UIC + select SERIAL config SAM460EX bool @@ -61,12 +60,10 @@ config SAM460EX select IDE_SII3112 select M41T80 select PPC440 - select SERIAL select SM501 select SMBUS_EEPROM select USB_EHCI_SYSBUS select USB_OHCI - select FDT_PPC config PREP bool @@ -123,26 +120,10 @@ config VIRTEX bool select PPC4XX select PFLASH_CFI01 - select SERIAL select XILINX select XILINX_ETHLITE select FDT_PPC -config XIVE - bool - depends on POWERNV || PSERIES - -config XIVE_SPAPR - bool - default y - depends on PSERIES - select XIVE - -config XIVE_KVM - bool - default y - depends on XIVE_SPAPR && KVM - # Only used by 64-bit targets config FW_CFG_PPC bool diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 665bc1784e..b156bcb999 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -33,6 +33,9 @@ #include "sysemu/qtest.h" #include "sysemu/reset.h" #include "hw/sysbus.h" +#include "hw/intc/ppc-uic.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" @@ -168,13 +171,13 @@ static void bamboo_init(MachineState *machine) MemoryRegion *ram_memories = g_new(MemoryRegion, PPC440EP_SDRAM_NR_BANKS); hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS]; hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS]; - qemu_irq *pic; - qemu_irq *irqs; PCIBus *pcibus; PowerPCCPU *cpu; CPUPPCState *env; target_long initrd_size = 0; DeviceState *dev; + DeviceState *uicdev; + SysBusDevice *uicsbd; int success; int i; @@ -192,10 +195,17 @@ static void bamboo_init(MachineState *machine) ppc_dcr_init(env, NULL, NULL); /* interrupt controller */ - irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); + uicdev = qdev_new(TYPE_PPC_UIC); + uicsbd = SYS_BUS_DEVICE(uicdev); + + object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu), + &error_fatal); + sysbus_realize_and_unref(uicsbd, &error_fatal); + + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); /* SDRAM controller */ memset(ram_bases, 0, sizeof(ram_bases)); @@ -203,14 +213,18 @@ static void bamboo_init(MachineState *machine) ppc4xx_sdram_banks(machine->ram, PPC440EP_SDRAM_NR_BANKS, ram_memories, ram_bases, ram_sizes, ppc440ep_sdram_bank_sizes); /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ - ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories, + ppc4xx_sdram_init(env, + qdev_get_gpio_in(uicdev, 14), + PPC440EP_SDRAM_NR_BANKS, ram_memories, ram_bases, ram_sizes, 1); /* PCI */ dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE, PPC440EP_PCI_CONFIG, - pic[pci_irq_nrs[0]], pic[pci_irq_nrs[1]], - pic[pci_irq_nrs[2]], pic[pci_irq_nrs[3]], + qdev_get_gpio_in(uicdev, pci_irq_nrs[0]), + qdev_get_gpio_in(uicdev, pci_irq_nrs[1]), + qdev_get_gpio_in(uicdev, pci_irq_nrs[2]), + qdev_get_gpio_in(uicdev, pci_irq_nrs[3]), NULL); pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); if (!pcibus) { @@ -223,12 +237,14 @@ static void bamboo_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); if (serial_hd(0) != NULL) { - serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], + serial_mm_init(address_space_mem, 0xef600300, 0, + qdev_get_gpio_in(uicdev, 0), PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } if (serial_hd(1) != NULL) { - serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], + serial_mm_init(address_space_mem, 0xef600400, 0, + qdev_get_gpio_in(uicdev, 1), PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index ee952314c8..91cbcd0504 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -169,7 +169,7 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, { struct PPC440PCIXState *s = opaque; - trace_ppc440_pcix_reg_read(addr, val); + trace_ppc440_pcix_reg_write(addr, val, size); switch (addr) { case PCI_VENDOR_ID ... PCI_MAX_LAT: stl_le_p(s->dev->config + addr, val); @@ -415,8 +415,15 @@ static void ppc440_pcix_reset(DeviceState *dev) s->sts = 0; } -/* All pins from each slot are tied to a single board IRQ. - * This may need further refactoring for other boards. */ +/* + * All four IRQ[ABCD] pins from all slots are tied to a single board + * IRQ, so our mapping function here maps everything to IRQ 0. + * The code in pci_change_irq_level() tracks the number of times + * the mapped IRQ is asserted and deasserted, so if multiple devices + * assert an IRQ at the same time the behaviour is correct. + * + * This may need further refactoring for boards that use multiple IRQ lines. + */ static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) { trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0); @@ -442,28 +449,35 @@ static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) return &s->bm_as; } -/* The default pci_host_data_{read,write} functions in pci/pci_host.c - * deny access to registers without bit 31 set but our clients want - * this to work so we have to override these here */ -static void pci_host_data_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) +/* + * Some guests on sam460ex write all kinds of garbage here such as + * missing enable bit and low bits set and still expect this to work + * (apparently it does on real hardware because these boot there) so + * we have to override these ops here and fix it up + */ +static void pci_host_config_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) { PCIHostState *s = opaque; - pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); + + if (addr != 0 || len != 4) { + return; + } + s->config_reg = (val & 0xfffffffcULL) | (1UL << 31); } -static uint64_t pci_host_data_read(void *opaque, - hwaddr addr, unsigned len) +static uint64_t pci_host_config_read(void *opaque, hwaddr addr, + unsigned len) { PCIHostState *s = opaque; - uint32_t val; - val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + uint32_t val = s->config_reg; + return val; } -const MemoryRegionOps ppc440_pcix_host_data_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, +const MemoryRegionOps ppc440_pcix_host_conf_ops = { + .read = pci_host_config_read, + .write = pci_host_config_write, .endianness = DEVICE_LITTLE_ENDIAN, }; @@ -490,9 +504,9 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); - memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, + memory_region_init_io(&h->conf_mem, OBJECT(s), &ppc440_pcix_host_conf_ops, h, "pci-conf-idx", 4); - memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops, + memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops, h, "pci-conf-data", 4); memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, "pci.reg", PPC440_REG_SIZE); diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index f1651e04d9..ffe4cf43e8 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -30,9 +30,12 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" #include "hw/boards.h" +#include "hw/intc/ppc-uic.h" +#include "hw/qdev-properties.h" #include "qemu/log.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" +#include "qapi/error.h" /*#define DEBUG_UIC*/ @@ -76,245 +79,40 @@ PowerPCCPU *ppc4xx_init(const char *cpu_type, /*****************************************************************************/ /* "Universal" Interrupt controller */ -enum { - DCR_UICSR = 0x000, - DCR_UICSRS = 0x001, - DCR_UICER = 0x002, - DCR_UICCR = 0x003, - DCR_UICPR = 0x004, - DCR_UICTR = 0x005, - DCR_UICMSR = 0x006, - DCR_UICVR = 0x007, - DCR_UICVCR = 0x008, - DCR_UICMAX = 0x009, -}; - -#define UIC_MAX_IRQ 32 -typedef struct ppcuic_t ppcuic_t; -struct ppcuic_t { - uint32_t dcr_base; - int use_vectors; - uint32_t level; /* Remembers the state of level-triggered interrupts. */ - uint32_t uicsr; /* Status register */ - uint32_t uicer; /* Enable register */ - uint32_t uiccr; /* Critical register */ - uint32_t uicpr; /* Polarity register */ - uint32_t uictr; /* Triggering register */ - uint32_t uicvcr; /* Vector configuration register */ - uint32_t uicvr; - qemu_irq *irqs; -}; - -static void ppcuic_trigger_irq (ppcuic_t *uic) -{ - uint32_t ir, cr; - int start, end, inc, i; - - /* Trigger interrupt if any is pending */ - ir = uic->uicsr & uic->uicer & (~uic->uiccr); - cr = uic->uicsr & uic->uicer & uic->uiccr; - LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 - " uiccr %08" PRIx32 "\n" - " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", - __func__, uic->uicsr, uic->uicer, uic->uiccr, - uic->uicsr & uic->uicer, ir, cr); - if (ir != 0x0000000) { - LOG_UIC("Raise UIC interrupt\n"); - qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]); - } else { - LOG_UIC("Lower UIC interrupt\n"); - qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]); - } - /* Trigger critical interrupt if any is pending and update vector */ - if (cr != 0x0000000) { - qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]); - if (uic->use_vectors) { - /* Compute critical IRQ vector */ - if (uic->uicvcr & 1) { - start = 31; - end = 0; - inc = -1; - } else { - start = 0; - end = 31; - inc = 1; - } - uic->uicvr = uic->uicvcr & 0xFFFFFFFC; - for (i = start; i <= end; i += inc) { - if (cr & (1 << i)) { - uic->uicvr += (i - start) * 512 * inc; - break; - } - } - } - LOG_UIC("Raise UIC critical interrupt - " - "vector %08" PRIx32 "\n", uic->uicvr); - } else { - LOG_UIC("Lower UIC critical interrupt\n"); - qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]); - uic->uicvr = 0x00000000; - } -} - -static void ppcuic_set_irq (void *opaque, int irq_num, int level) -{ - ppcuic_t *uic; - uint32_t mask, sr; - - uic = opaque; - mask = 1U << (31-irq_num); - LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 - " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", - __func__, irq_num, level, - uic->uicsr, mask, uic->uicsr & mask, level << irq_num); - if (irq_num < 0 || irq_num > 31) - return; - sr = uic->uicsr; - - /* Update status register */ - if (uic->uictr & mask) { - /* Edge sensitive interrupt */ - if (level == 1) - uic->uicsr |= mask; - } else { - /* Level sensitive interrupt */ - if (level == 1) { - uic->uicsr |= mask; - uic->level |= mask; - } else { - uic->uicsr &= ~mask; - uic->level &= ~mask; - } - } - LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " - "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); - if (sr != uic->uicsr) - ppcuic_trigger_irq(uic); -} - -static uint32_t dcr_read_uic (void *opaque, int dcrn) -{ - ppcuic_t *uic; - uint32_t ret; - - uic = opaque; - dcrn -= uic->dcr_base; - switch (dcrn) { - case DCR_UICSR: - case DCR_UICSRS: - ret = uic->uicsr; - break; - case DCR_UICER: - ret = uic->uicer; - break; - case DCR_UICCR: - ret = uic->uiccr; - break; - case DCR_UICPR: - ret = uic->uicpr; - break; - case DCR_UICTR: - ret = uic->uictr; - break; - case DCR_UICMSR: - ret = uic->uicsr & uic->uicer; - break; - case DCR_UICVR: - if (!uic->use_vectors) - goto no_read; - ret = uic->uicvr; - break; - case DCR_UICVCR: - if (!uic->use_vectors) - goto no_read; - ret = uic->uicvcr; - break; - default: - no_read: - ret = 0x00000000; - break; - } - - return ret; -} - -static void dcr_write_uic (void *opaque, int dcrn, uint32_t val) -{ - ppcuic_t *uic; - - uic = opaque; - dcrn -= uic->dcr_base; - LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); - switch (dcrn) { - case DCR_UICSR: - uic->uicsr &= ~val; - uic->uicsr |= uic->level; - ppcuic_trigger_irq(uic); - break; - case DCR_UICSRS: - uic->uicsr |= val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICER: - uic->uicer = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICCR: - uic->uiccr = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICPR: - uic->uicpr = val; - break; - case DCR_UICTR: - uic->uictr = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICMSR: - break; - case DCR_UICVR: - break; - case DCR_UICVCR: - uic->uicvcr = val & 0xFFFFFFFD; - ppcuic_trigger_irq(uic); - break; - } -} - -static void ppcuic_reset (void *opaque) -{ - ppcuic_t *uic; - - uic = opaque; - uic->uiccr = 0x00000000; - uic->uicer = 0x00000000; - uic->uicpr = 0x00000000; - uic->uicsr = 0x00000000; - uic->uictr = 0x00000000; - if (uic->use_vectors) { - uic->uicvcr = 0x00000000; - uic->uicvr = 0x0000000; - } -} qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, uint32_t dcr_base, int has_ssr, int has_vr) { - ppcuic_t *uic; + DeviceState *uicdev = qdev_new(TYPE_PPC_UIC); + SysBusDevice *uicsbd = SYS_BUS_DEVICE(uicdev); + qemu_irq *uic_irqs; int i; - uic = g_malloc0(sizeof(ppcuic_t)); - uic->dcr_base = dcr_base; - uic->irqs = irqs; - if (has_vr) - uic->use_vectors = 1; - for (i = 0; i < DCR_UICMAX; i++) { - ppc_dcr_register(env, dcr_base + i, uic, - &dcr_read_uic, &dcr_write_uic); + qdev_prop_set_uint32(uicdev, "dcr-base", dcr_base); + qdev_prop_set_bit(uicdev, "use-vectors", has_vr); + object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(env_cpu(env)), + &error_fatal); + sysbus_realize_and_unref(uicsbd, &error_fatal); + + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, irqs[PPCUIC_OUTPUT_INT]); + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, irqs[PPCUIC_OUTPUT_CINT]); + + /* + * Return an allocated array of the UIC's input IRQ lines. + * This is an ugly temporary API to retain compatibility with + * the ppcuic_init() interface from the pre-QOM-conversion UIC. + * None of the callers free this array, so it is leaked -- but + * so was the array allocated by qemu_allocate_irqs() in the + * old code. + * + * The callers should just instantiate the UIC and wire it up + * themselves rather than passing qemu_irq* in and out of this function. + */ + uic_irqs = g_new0(qemu_irq, UIC_MAX_IRQ); + for (i = 0; i < UIC_MAX_IRQ; i++) { + uic_irqs[i] = qdev_get_gpio_in(uicdev, i); } - qemu_register_reset(ppcuic_reset, uic); - - return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ); + return uic_irqs; } /*****************************************************************************/ diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index 28724c06f8..e8789f64e8 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/ppc/spapr.c b/hw/ppc/spapr.c index 489cefcb81..2c403b574e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1120,6 +1120,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + uint32_t root_drc_type_mask = 0; int ret; void *fdt; SpaprPhbState *phb; @@ -1194,8 +1195,18 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) spapr_dt_cpus(fdt, spapr); + /* ibm,drc-indexes and friends */ if (smc->dr_lmb_enabled) { - _FDT(spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_LMB; + } + if (smc->dr_phb_enabled) { + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PHB; + } + if (mc->nvdimm_supported) { + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PMEM; + } + if (root_drc_type_mask) { + _FDT(spapr_dt_drc(fdt, 0, NULL, root_drc_type_mask)); } if (mc->has_hotpluggable_cpus) { @@ -1233,14 +1244,6 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) } } - if (smc->dr_phb_enabled) { - ret = spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB); - if (ret < 0) { - error_report("Couldn't set up PHB DR device tree properties"); - exit(1); - } - } - /* NVDIMM devices */ if (mc->nvdimm_supported) { spapr_dt_persistent_memory(spapr, fdt); @@ -1563,19 +1566,6 @@ void spapr_setup_hpt(SpaprMachineState *spapr) } } -static int spapr_reset_drcs(Object *child, void *opaque) -{ - SpaprDrc *drc = - (SpaprDrc *) object_dynamic_cast(child, - TYPE_SPAPR_DR_CONNECTOR); - - if (drc) { - spapr_drc_reset(drc); - } - - return 0; -} - static void spapr_machine_reset(MachineState *machine) { SpaprMachineState *spapr = SPAPR_MACHINE(machine); @@ -1630,7 +1620,7 @@ static void spapr_machine_reset(MachineState *machine) * will crash QEMU if the DIMM holding the vring goes away). To avoid such * situations, we reset DRCs after all devices have been reset. */ - object_child_foreach_recursive(object_get_root(), spapr_reset_drcs, NULL); + spapr_drc_reset_all(spapr); spapr_clear_pending_events(spapr); @@ -3437,6 +3427,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, if (dedicated_hp_event_source) { drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr_start / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs, spapr_drc_index(drc)); @@ -3677,6 +3668,7 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr_start / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs, spapr_drc_index(drc)); } @@ -4064,7 +4056,8 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { + if (!smc->pre_6_0_memory_unplug || + spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { spapr_memory_unplug_request(hotplug_dev, dev, errp); } else { /* NOTE: this means there is a window after guest reset, prior to @@ -4550,8 +4543,11 @@ DEFINE_SPAPR_MACHINE(6_0, "6.0", true); */ static void spapr_machine_5_2_class_options(MachineClass *mc) { + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + spapr_machine_6_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); + smc->pre_6_0_memory_unplug = true; } DEFINE_SPAPR_MACHINE(5_2, "5.2", false); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index f991cf89a0..8571d5bafe 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -417,9 +417,10 @@ void spapr_drc_detach(SpaprDrc *drc) spapr_drc_release(drc); } -void spapr_drc_reset(SpaprDrc *drc) +bool spapr_drc_reset(SpaprDrc *drc) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + bool unplug_completed = false; trace_spapr_drc_reset(spapr_drc_index(drc)); @@ -428,6 +429,7 @@ void spapr_drc_reset(SpaprDrc *drc) */ if (drc->unplug_requested) { spapr_drc_release(drc); + unplug_completed = true; } if (drc->dev) { @@ -444,6 +446,8 @@ void spapr_drc_reset(SpaprDrc *drc) drc->ccs_offset = -1; drc->ccs_depth = -1; } + + return unplug_completed; } static bool spapr_drc_unplug_requested_needed(void *opaque) @@ -462,8 +466,9 @@ static const VMStateDescription vmstate_spapr_drc_unplug_requested = { } }; -bool spapr_drc_transient(SpaprDrc *drc) +static bool spapr_drc_needed(void *opaque) { + SpaprDrc *drc = opaque; SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); /* @@ -483,11 +488,6 @@ bool spapr_drc_transient(SpaprDrc *drc) spapr_drc_unplug_requested(drc); } -static bool spapr_drc_needed(void *opaque) -{ - return spapr_drc_transient(opaque); -} - static const VMStateDescription vmstate_spapr_drc = { .name = "spapr_drc", .version_id = 1, @@ -503,7 +503,7 @@ static const VMStateDescription vmstate_spapr_drc = { } }; -static void realize(DeviceState *d, Error **errp) +static void drc_realize(DeviceState *d, Error **errp) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); Object *root_container; @@ -530,7 +530,7 @@ static void realize(DeviceState *d, Error **errp) trace_spapr_drc_realize_complete(spapr_drc_index(drc)); } -static void unrealize(DeviceState *d) +static void drc_unrealize(DeviceState *d) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); Object *root_container; @@ -579,8 +579,8 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data) { DeviceClass *dk = DEVICE_CLASS(k); - dk->realize = realize; - dk->unrealize = unrealize; + dk->realize = drc_realize; + dk->unrealize = drc_unrealize; /* * Reason: DR connector needs to be wired to either the machine or to a * PHB in spapr_dr_connector_new(). @@ -628,7 +628,7 @@ static void realize_physical(DeviceState *d, Error **errp) SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d); Error *local_err = NULL; - realize(d, &local_err); + drc_realize(d, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -644,7 +644,7 @@ static void unrealize_physical(DeviceState *d) { SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d); - unrealize(d); + drc_unrealize(d); vmstate_unregister(VMSTATE_IF(drcp), &vmstate_spapr_drc_physical, drcp); qemu_unregister_reset(drc_physical_reset, drcp); } @@ -832,6 +832,12 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) GString *drc_names, *drc_types; int ret; + /* + * This should really be only called once per node since it overwrites + * the OF properties if they already exist. + */ + g_assert(!fdt_get_property(fdt, offset, "ibm,drc-indexes", NULL)); + /* the first entry of each properties is a 32-bit integer encoding * the number of elements in the array. we won't know this until * we complete the iteration through all the matching DRCs, but @@ -943,6 +949,37 @@ out: return ret; } +void spapr_drc_reset_all(SpaprMachineState *spapr) +{ + Object *drc_container; + ObjectProperty *prop; + ObjectPropertyIterator iter; + + drc_container = container_get(object_get_root(), DRC_CONTAINER_PATH); +restart: + object_property_iter_init(&iter, drc_container); + while ((prop = object_property_iter_next(&iter))) { + SpaprDrc *drc; + + if (!strstart(prop->type, "link<", NULL)) { + continue; + } + drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container, + prop->name, + &error_abort)); + + /* + * This will complete any pending plug/unplug requests. + * In case of a unplugged PHB or PCI bridge, this will + * cause some DRCs to be destroyed and thus potentially + * invalidate the iterator. + */ + if (spapr_drc_reset(drc)) { + goto restart; + } + } +} + /* * RTAS calls */ diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 3f37b49fd8..6aedd988b3 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -658,7 +658,8 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, /* we should not be using count_indexed value unless the guest * supports dedicated hotplug event source */ - g_assert(spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)); + g_assert(!SPAPR_MACHINE_GET_CLASS(spapr)->pre_6_0_memory_unplug || + spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)); hp->drc_id.count_indexed.count = cpu_to_be32(drc_id->count_indexed.count); hp->drc_id.count_indexed.index = diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index c0ea0bd579..e5dfc1ba7a 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1632,32 +1632,6 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, uint32_t max_compat, return best_compat; } -static void spapr_handle_transient_dev_before_cas(SpaprMachineState *spapr) -{ - Object *drc_container; - ObjectProperty *prop; - ObjectPropertyIterator iter; - - drc_container = container_get(object_get_root(), "/dr-connector"); - object_property_iter_init(&iter, drc_container); - while ((prop = object_property_iter_next(&iter))) { - SpaprDrc *drc; - - if (!strstart(prop->type, "link<", NULL)) { - continue; - } - drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container, - prop->name, - &error_abort)); - - if (spapr_drc_transient(drc)) { - spapr_drc_reset(drc); - } - } - - spapr_clear_pending_hotplug_events(spapr); -} - target_ulong do_client_architecture_support(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong vec, @@ -1815,7 +1789,12 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, spapr_irq_update_active_intc(spapr); - spapr_handle_transient_dev_before_cas(spapr); + /* + * Process all pending hot-plug/unplug requests now. An updated full + * rendered FDT will be returned to the guest. + */ + spapr_drc_reset_all(spapr); + spapr_clear_pending_hotplug_events(spapr); /* * If spapr_machine_reset() did not set up a HPT but one is necessary diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 6d8d095aa2..1e91984526 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -96,3 +96,4 @@ ppc440_pcix_set_irq(int irq_num) "PCI irq %d" ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64 ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64 ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 +ppc440_pcix_reg_write(uint64_t addr, uint32_t val, uint32_t size) "addr 0x%" PRIx64 " = 0x%" PRIx32 " size 0x%" PRIx32 diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 07fe49da0d..b26ff17767 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -43,6 +43,7 @@ #include "qemu/option.h" #include "exec/address-spaces.h" +#include "hw/intc/ppc-uic.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" #include "hw/qdev-properties.h" @@ -95,7 +96,8 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) { PowerPCCPU *cpu; CPUPPCState *env; - qemu_irq *irqs; + DeviceState *uicdev; + SysBusDevice *uicsbd; cpu = POWERPC_CPU(cpu_create(cpu_type)); env = &cpu->env; @@ -105,10 +107,19 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) ppc_dcr_init(env, NULL, NULL); /* interrupt controller */ - irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - ppcuic_init(env, irqs, 0x0C0, 0, 1); + uicdev = qdev_new(TYPE_PPC_UIC); + uicsbd = SYS_BUS_DEVICE(uicdev); + + object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu), + &error_fatal); + sysbus_realize_and_unref(uicsbd, &error_fatal); + + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + + /* This board doesn't wire anything up to the inputs of the UIC. */ return cpu; } diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c index 73d2d0bccb..734892f47c 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/hw/sparc/Kconfig b/hw/sparc/Kconfig index 91805afab6..8dcb10086f 100644 --- a/hw/sparc/Kconfig +++ b/hw/sparc/Kconfig @@ -14,6 +14,7 @@ config SUN4M select M48T59 select STP2000 select CHRP_NVRAM + select OR_IRQ config LEON3 bool diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 4bc4ebea84..7e16eea9e6 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -52,8 +52,6 @@ #define LEON3_PROM_OFFSET (0x00000000) #define LEON3_RAM_OFFSET (0x40000000) -#define MAX_PILS 16 - #define LEON3_UART_OFFSET (0x80000100) #define LEON3_UART_IRQ (3) @@ -194,11 +192,10 @@ static void leon3_generic_hw_init(MachineState *machine) MemoryRegion *prom = g_new(MemoryRegion, 1); int ret; char *filename; - qemu_irq *cpu_irqs = NULL; int bios_size; int prom_size; ResetData *reset_info; - DeviceState *dev; + DeviceState *dev, *irqmpdev; int i; AHBPnp *ahb_pnp; APBPnp *apb_pnp; @@ -230,16 +227,15 @@ static void leon3_generic_hw_init(MachineState *machine) GRLIB_AHB_SLAVE, GRLIB_AHBMEM_AREA); /* Allocate IRQ manager */ - dev = qdev_new(TYPE_GRLIB_IRQMP); + irqmpdev = qdev_new(TYPE_GRLIB_IRQMP); qdev_init_gpio_in_named_with_opaque(DEVICE(cpu), leon3_set_pil_in, env, "pil", 1); - qdev_connect_gpio_out_named(dev, "grlib-irq", 0, + qdev_connect_gpio_out_named(irqmpdev, "grlib-irq", 0, qdev_get_gpio_in_named(DEVICE(cpu), "pil", 0)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, LEON3_IRQMP_OFFSET); - env->irq_manager = dev; + sysbus_realize_and_unref(SYS_BUS_DEVICE(irqmpdev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(irqmpdev), 0, LEON3_IRQMP_OFFSET); + env->irq_manager = irqmpdev; env->qemu_irq_ack = leon3_irq_manager; - cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq, dev, MAX_PILS); grlib_apb_pnp_add_entry(apb_pnp, LEON3_IRQMP_OFFSET, 0xFFF, GRLIB_VENDOR_GAISLER, GRLIB_IRQMP_DEV, 2, 0, GRLIB_APBIO_AREA); @@ -330,7 +326,7 @@ static void leon3_generic_hw_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, LEON3_TIMER_OFFSET); for (i = 0; i < LEON3_TIMER_COUNT; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, - cpu_irqs[LEON3_TIMER_IRQ + i]); + qdev_get_gpio_in(irqmpdev, LEON3_TIMER_IRQ + i)); } grlib_apb_pnp_add_entry(apb_pnp, LEON3_TIMER_OFFSET, 0xFFF, @@ -342,7 +338,8 @@ static void leon3_generic_hw_init(MachineState *machine) qdev_prop_set_chr(dev, "chrdev", serial_hd(0)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, LEON3_UART_OFFSET); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irqs[LEON3_UART_IRQ]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(irqmpdev, LEON3_UART_IRQ)); grlib_apb_pnp_add_entry(apb_pnp, LEON3_UART_OFFSET, 0xFFF, GRLIB_VENDOR_GAISLER, GRLIB_APBUART_DEV, 1, LEON3_UART_IRQ, GRLIB_APBIO_AREA); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 8686371318..38ca1e33c7 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -50,6 +50,7 @@ #include "hw/misc/empty_slot.h" #include "hw/misc/unimp.h" #include "hw/irq.h" +#include "hw/or-irq.h" #include "hw/loader.h" #include "elf.h" #include "trace.h" @@ -848,7 +849,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, uint32_t initrd_size; DriveInfo *fd[MAX_FD]; FWCfgState *fw_cfg; - DeviceState *dev; + DeviceState *dev, *ms_kb_orgate, *serial_orgate; SysBusDevice *s; unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; @@ -994,10 +995,16 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, qdev_prop_set_uint32(dev, "chnAtype", escc_kbd); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); - sysbus_connect_irq(s, 0, slavio_irq[14]); - sysbus_connect_irq(s, 1, slavio_irq[14]); sysbus_mmio_map(s, 0, hwdef->ms_kb_base); + /* Logically OR both its IRQs together */ + ms_kb_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + object_property_set_int(OBJECT(ms_kb_orgate), "num-lines", 2, &error_fatal); + qdev_realize_and_unref(ms_kb_orgate, NULL, &error_fatal); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(ms_kb_orgate, 0)); + sysbus_connect_irq(s, 1, qdev_get_gpio_in(ms_kb_orgate, 1)); + qdev_connect_gpio_out(DEVICE(ms_kb_orgate), 0, slavio_irq[14]); + dev = qdev_new(TYPE_ESCC); qdev_prop_set_uint32(dev, "disabled", 0); qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); @@ -1009,10 +1016,17 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); - sysbus_connect_irq(s, 0, slavio_irq[15]); - sysbus_connect_irq(s, 1, slavio_irq[15]); sysbus_mmio_map(s, 0, hwdef->serial_base); + /* Logically OR both its IRQs together */ + serial_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + object_property_set_int(OBJECT(serial_orgate), "num-lines", 2, + &error_fatal); + qdev_realize_and_unref(serial_orgate, NULL, &error_fatal); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(serial_orgate, 0)); + sysbus_connect_irq(s, 1, qdev_get_gpio_in(serial_orgate, 1)); + qdev_connect_gpio_out(DEVICE(serial_orgate), 0, slavio_irq[15]); + if (hwdef->apc_base) { apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0)); } diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 5b2d20cb6a..03e33fc592 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -332,6 +332,10 @@ static const MemoryRegionOps slavio_timer_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, .max_access_size = 4, }, }; diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h index a0a8d9aa46..659f9298e8 100644 --- a/include/exec/helper-proto.h +++ b/include/exec/helper-proto.h @@ -35,11 +35,15 @@ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ dh_ctype(t4), dh_ctype(t5), dh_ctype(t6), \ dh_ctype(t7)); +#define IN_HELPER_PROTO + #include "helper.h" #include "trace/generated-helpers.h" #include "tcg-runtime.h" #include "plugin-helpers.h" +#undef IN_HELPER_PROTO + #undef DEF_HELPER_FLAGS_0 #undef DEF_HELPER_FLAGS_1 #undef DEF_HELPER_FLAGS_2 diff --git a/include/hw/clock.h b/include/hw/clock.h index 81bcf3e505..6382f34656 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 0000000000..848e65eb35 --- /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/intc/ppc-uic.h b/include/hw/intc/ppc-uic.h new file mode 100644 index 0000000000..e614e2ffd8 --- /dev/null +++ b/include/hw/intc/ppc-uic.h @@ -0,0 +1,73 @@ +/* + * "Universal" Interrupt Controller for PowerPPC 4xx embedded processors + * + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_INTC_PPC_UIC_H +#define HW_INTC_PPC_UIC_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_PPC_UIC "ppc-uic" +OBJECT_DECLARE_SIMPLE_TYPE(PPCUIC, PPC_UIC) + +/* + * QEMU interface: + * QOM property "cpu": link to the PPC CPU + * (no default, must be set) + * QOM property "dcr-base": base of the bank of DCR registers for the UIC + * (default 0x30) + * QOM property "use-vectors": true if the UIC has vector registers + * (default true) + * unnamed GPIO inputs 0..UIC_MAX_IRQ: input IRQ lines + * sysbus IRQs: + * 0 (PPCUIC_OUTPUT_INT): output INT line to the CPU + * 1 (PPCUIC_OUTPUT_CINT): output CINT line to the CPU + */ + +#define UIC_MAX_IRQ 32 + +struct PPCUIC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + qemu_irq output_int; + qemu_irq output_cint; + + /* properties */ + CPUState *cpu; + uint32_t dcr_base; + bool use_vectors; + + uint32_t level; /* Remembers the state of level-triggered interrupts. */ + uint32_t uicsr; /* Status register */ + uint32_t uicer; /* Enable register */ + uint32_t uiccr; /* Critical register */ + uint32_t uicpr; /* Polarity register */ + uint32_t uictr; /* Triggering register */ + uint32_t uicvcr; /* Vector configuration register */ + uint32_t uicvr; +}; + +#endif diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h index f23f45dfb1..5b0a1ffe72 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/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 4f58f0223b..bd014823a9 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -115,8 +115,6 @@ struct SpaprPhbState { #define SPAPR_PCI_NV2RAM64_WIN_BASE SPAPR_PCI_LIMIT #define SPAPR_PCI_NV2RAM64_WIN_SIZE (2 * TiB) /* For up to 6 GPUs 256GB each */ -/* Max number of these GPUsper a physical box */ -#define NVGPU_MAX_NUM 6 /* Max number of NVLinks per GPU in any physical box */ #define NVGPU_MAX_LINKS 3 diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index e0f10f252c..1cc19575f5 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -112,6 +112,9 @@ typedef enum { #define NUMA_ASSOC_SIZE (MAX_DISTANCE_REF_POINTS + 1) #define VCPU_ASSOC_SIZE (NUMA_ASSOC_SIZE + 1) +/* Max number of these GPUsper a physical box */ +#define NVGPU_MAX_NUM 6 + typedef struct SpaprCapabilities SpaprCapabilities; struct SpaprCapabilities { uint8_t caps[SPAPR_CAP_NUM]; @@ -139,6 +142,7 @@ struct SpaprMachineClass { hwaddr rma_limit; /* clamp the RMA to this size */ bool pre_5_1_assoc_refpoints; bool pre_5_2_numa_associativity; + bool pre_6_0_memory_unplug; bool (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, @@ -239,7 +243,7 @@ struct SpaprMachineState { unsigned gpu_numa_id; SpaprTpmProxy *tpm_proxy; - uint32_t numa_assoc_array[MAX_NODES][NUMA_ASSOC_SIZE]; + uint32_t numa_assoc_array[MAX_NODES + NVGPU_MAX_NUM][NUMA_ASSOC_SIZE]; Error *fwnmi_migration_blocker; }; diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h index def3593adc..8982927d5c 100644 --- a/include/hw/ppc/spapr_drc.h +++ b/include/hw/ppc/spapr_drc.h @@ -224,7 +224,8 @@ static inline bool spapr_drc_hotplugged(DeviceState *dev) return dev->hotplugged && !runstate_check(RUN_STATE_INMIGRATE); } -void spapr_drc_reset(SpaprDrc *drc); +/* Returns true if an unplug request completed */ +bool spapr_drc_reset(SpaprDrc *drc); uint32_t spapr_drc_index(SpaprDrc *drc); SpaprDrcType spapr_drc_type(SpaprDrc *drc); @@ -244,8 +245,11 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask); void spapr_drc_attach(SpaprDrc *drc, DeviceState *d); void spapr_drc_detach(SpaprDrc *drc); -/* Returns true if a hot plug/unplug request is pending */ -bool spapr_drc_transient(SpaprDrc *drc); +/* + * Reset all DRCs, causing pending hot-plug/unplug requests to complete. + * Safely handles potential DRC removal (eg. PHBs or PCI bridges). + */ +void spapr_drc_reset_all(struct SpaprMachineState *spapr); static inline bool spapr_drc_unplug_requested(SpaprDrc *drc) { diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h index 26c8d90d71..b282960ad9 100644 --- a/include/hw/ppc/spapr_xive.h +++ b/include/hw/ppc/spapr_xive.h @@ -66,8 +66,6 @@ typedef struct SpaprXiveClass { */ #define SPAPR_XIVE_BLOCK_ID 0x0 -void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon); - struct SpaprMachineState; void spapr_xive_hcall_init(struct SpaprMachineState *spapr); void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable); diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h index 78b6178fcd..2104f493f3 100644 --- a/include/hw/sparc/grlib.h +++ b/include/hw/sparc/grlib.h @@ -34,10 +34,6 @@ /* IRQMP */ #define TYPE_GRLIB_IRQMP "grlib,irqmp" -typedef void (*set_pil_in_fn) (void *opaque, uint32_t pil_in); - -void grlib_irqmp_set_irq(void *opaque, int irq, int level); - void grlib_irqmp_ack(DeviceState *dev, int intno); /* GPTimer */ diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 5abf17fecc..5b3bdacc39 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -1085,6 +1085,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_bswap16_tl tcg_gen_bswap16_i64 #define tcg_gen_bswap32_tl tcg_gen_bswap32_i64 #define tcg_gen_bswap64_tl tcg_gen_bswap64_i64 +#define tcg_gen_bswap_tl tcg_gen_bswap64_i64 #define tcg_gen_concat_tl_i64 tcg_gen_concat32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr32_i64 #define tcg_gen_andc_tl tcg_gen_andc_i64 @@ -1197,6 +1198,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_ext32s_tl tcg_gen_mov_i32 #define tcg_gen_bswap16_tl tcg_gen_bswap16_i32 #define tcg_gen_bswap32_tl tcg_gen_bswap32_i32 +#define tcg_gen_bswap_tl tcg_gen_bswap32_i32 #define tcg_gen_concat_tl_i64 tcg_gen_concat_i32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr_i64_i32 #define tcg_gen_andc_tl tcg_gen_andc_i32 diff --git a/meson.build b/meson.build index dd618907c0..563688d682 100644 --- a/meson.build +++ b/meson.build @@ -2148,7 +2148,6 @@ foreach target : target_dirs custom_target(exe['name'] + stp['ext'], input: trace_events_all, output: exe['name'] + stp['ext'], - capture: true, install: stp['install'], install_dir: get_option('datadir') / 'systemtap/tapset', command: [ @@ -2157,7 +2156,7 @@ foreach target : target_dirs '--target-name=' + target_name, '--target-type=' + target_type, '--probe-prefix=qemu.' + target_type + '.' + target_name, - '@INPUT@', + '@INPUT@', '@OUTPUT@' ]) endforeach endif diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 31146242b7..ab7653a5ce 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -16,7 +16,7 @@ __email__ = "stefanha@redhat.com" import sys import getopt -from tracetool import error_write, out +from tracetool import error_write, out, out_open import tracetool.backend import tracetool.format @@ -32,7 +32,7 @@ def error_opt(msg = None): format_descr = "\n".join([ " %-15s %s" % (n, d) for n,d in tracetool.format.get_list() ]) error_write("""\ -Usage: %(script)s --format=<format> --backends=<backends> [<options>] +Usage: %(script)s --format=<format> --backends=<backends> [<options>] <trace-events> ... <output> Backends: %(backends)s @@ -135,13 +135,15 @@ def main(args): if probe_prefix is None: probe_prefix = ".".join(["qemu", target_type, target_name]) - if len(args) < 1: - error_opt("missing trace-events filepath") + if len(args) < 2: + error_opt("missing trace-events and output filepaths") events = [] - for arg in args: + for arg in args[:-1]: with open(arg, "r") as fh: events.extend(tracetool.read_events(fh, arg)) + out_open(args[-1]) + try: tracetool.generate(events, arg_group, arg_format, arg_backends, binary=binary, probe_prefix=probe_prefix) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 3ee54be223..96b1cd69a5 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -31,14 +31,36 @@ def error(*lines): sys.exit(1) +out_lineno = 1 +out_filename = '<none>' +out_fobj = sys.stdout + +def out_open(filename): + global out_filename, out_fobj + out_filename = filename + out_fobj = open(filename, 'wt') + def out(*lines, **kwargs): """Write a set of output lines. You can use kwargs as a shorthand for mapping variables when formatting all the strings in lines. + + The 'out_lineno' kwarg is automatically added to reflect the current output + file line number. The 'out_next_lineno' kwarg is also automatically added + with the next output line number. The 'out_filename' kwarg is automatically + added with the output filename. """ - lines = [ l % kwargs for l in lines ] - sys.stdout.writelines("\n".join(lines) + "\n") + global out_lineno + output = [] + for l in lines: + kwargs['out_lineno'] = out_lineno + kwargs['out_next_lineno'] = out_lineno + 1 + kwargs['out_filename'] = out_filename + output.append(l % kwargs) + out_lineno += 1 + + out_fobj.writelines("\n".join(output) + "\n") # We only want to allow standard C types or fixed sized # integer types. We don't want QEMU specific types @@ -196,6 +218,10 @@ class Event(object): Properties of the event. args : Arguments The event arguments. + lineno : int + The line number in the input file. + filename : str + The path to the input file. """ @@ -208,7 +234,7 @@ class Event(object): _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"]) - def __init__(self, name, props, fmt, args, orig=None, + def __init__(self, name, props, fmt, args, lineno, filename, orig=None, event_trans=None, event_exec=None): """ Parameters @@ -221,6 +247,10 @@ class Event(object): Event printing format string(s). args : Arguments Event arguments. + lineno : int + The line number in the input file. + filename : str + The path to the input file. orig : Event or None Original Event before transformation/generation. event_trans : Event or None @@ -233,6 +263,8 @@ class Event(object): self.properties = props self.fmt = fmt self.args = args + self.lineno = int(lineno) + self.filename = str(filename) self.event_trans = event_trans self.event_exec = event_exec @@ -254,16 +286,21 @@ class Event(object): def copy(self): """Create a new copy.""" return Event(self.name, list(self.properties), self.fmt, - self.args.copy(), self, self.event_trans, self.event_exec) + self.args.copy(), self.lineno, self.filename, + self, self.event_trans, self.event_exec) @staticmethod - def build(line_str): + def build(line_str, lineno, filename): """Build an Event instance from a string. Parameters ---------- line_str : str Line describing the event. + lineno : int + Line number in input file. + filename : str + Path to input file. """ m = Event._CRE.match(line_str) assert m is not None @@ -293,7 +330,7 @@ class Event(object): if "tcg" in props and isinstance(fmt, str): raise ValueError("Events with 'tcg' property must have two format strings") - event = Event(name, props, fmt, args) + event = Event(name, props, fmt, args, lineno, filename) # add implicit arguments when using the 'vcpu' property import tracetool.vcpu @@ -338,6 +375,8 @@ class Event(object): list(self.properties), self.fmt, self.args.transform(*trans), + self.lineno, + self.filename, self) @@ -364,7 +403,7 @@ def read_events(fobj, fname): continue try: - event = Event.build(line) + event = Event.build(line, lineno, fname) except ValueError as e: arg0 = 'Error at %s:%d: %s' % (fname, lineno, e.args[0]) e.args = (arg0,) + e.args[1:] diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index e9844dd335..5fa30ccc08 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -33,8 +33,10 @@ def generate_h(event, group): ' int unused __attribute__ ((unused));', ' int trlen;', ' if (trace_event_get_state(%(event_id)s)) {', + '#line %(event_lineno)d "%(event_filename)s"', ' trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN,', ' "%(name)s " %(fmt)s "\\n" %(argnames)s);', + '#line %(out_next_lineno)d "%(out_filename)s"', ' trlen = MIN(trlen, MAX_TRACE_STRLEN - 1);', ' unused = write(trace_marker_fd, ftrace_buf, trlen);', ' }', @@ -42,6 +44,8 @@ def generate_h(event, group): name=event.name, args=event.args, event_id="TRACE_" + event.name.upper(), + event_lineno=event.lineno, + event_filename=event.filename, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 877222bbe9..bc43dbb4f4 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -37,12 +37,16 @@ def generate_h(event, group): out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', ' struct timeval _now;', ' gettimeofday(&_now, NULL);', + '#line %(event_lineno)d "%(event_filename)s"', ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",', ' qemu_get_thread_id(),', ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', ' %(argnames)s);', + '#line %(out_next_lineno)d "%(out_filename)s"', ' }', cond=cond, + event_lineno=event.lineno, + event_filename=event.filename, name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 1373a90192..5a3a00fe31 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -35,9 +35,13 @@ def generate_h(event, group): cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s) {', + '#line %(event_lineno)d "%(event_filename)s"', ' syslog(LOG_INFO, "%(name)s " %(fmt)s %(argnames)s);', + '#line %(out_next_lineno)d "%(out_filename)s"', ' }', cond=cond, + event_lineno=event.lineno, + event_filename=event.filename, name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 2c57e36c9a..8dc656becc 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 b2cd69ff7f..2283214c87 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/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index d536f3ccc1..4089e29cd9 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1462,14 +1462,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_shl_i32: if (c2) { - tcg_out_opc_imm(s, OPC_SLLIW, a0, a1, a2); + tcg_out_opc_imm(s, OPC_SLLIW, a0, a1, a2 & 0x1f); } else { tcg_out_opc_reg(s, OPC_SLLW, a0, a1, a2); } break; case INDEX_op_shl_i64: if (c2) { - tcg_out_opc_imm(s, OPC_SLLI, a0, a1, a2); + tcg_out_opc_imm(s, OPC_SLLI, a0, a1, a2 & 0x3f); } else { tcg_out_opc_reg(s, OPC_SLL, a0, a1, a2); } @@ -1477,14 +1477,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_shr_i32: if (c2) { - tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2); + tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2 & 0x1f); } else { tcg_out_opc_reg(s, OPC_SRLW, a0, a1, a2); } break; case INDEX_op_shr_i64: if (c2) { - tcg_out_opc_imm(s, OPC_SRLI, a0, a1, a2); + tcg_out_opc_imm(s, OPC_SRLI, a0, a1, a2 & 0x3f); } else { tcg_out_opc_reg(s, OPC_SRL, a0, a1, a2); } @@ -1492,14 +1492,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_sar_i32: if (c2) { - tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2); + tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2 & 0x1f); } else { tcg_out_opc_reg(s, OPC_SRAW, a0, a1, a2); } break; case INDEX_op_sar_i64: if (c2) { - tcg_out_opc_imm(s, OPC_SRAI, a0, a1, a2); + tcg_out_opc_imm(s, OPC_SRAI, a0, a1, a2 & 0x3f); } else { tcg_out_opc_reg(s, OPC_SRA, a0, a1, a2); } diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index ddbe06b71a..1a41dfa908 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -547,6 +547,9 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, in_c = dup_const(vece, in_c); if (in_c == 0) { oprsz = maxsz; + vece = MO_8; + } else if (in_c == dup_const(MO_8, in_c)) { + vece = MO_8; } } @@ -628,6 +631,35 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, /* Otherwise implement out of line. */ t_ptr = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_ptr, cpu_env, dofs); + + /* + * This may be expand_clr for the tail of an operation, e.g. + * oprsz == 8 && maxsz == 64. The size of the clear is misaligned + * wrt simd_desc and will assert. Simply pass all replicated byte + * stores through to memset. + */ + if (oprsz == maxsz && vece == MO_8) { + TCGv_ptr t_size = tcg_const_ptr(oprsz); + TCGv_i32 t_val; + + if (in_32) { + t_val = in_32; + } else if (in_64) { + t_val = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t_val, in_64); + } else { + t_val = tcg_const_i32(in_c); + } + gen_helper_memset(t_ptr, t_ptr, t_val, t_size); + + if (!in_32) { + tcg_temp_free_i32(t_val); + } + tcg_temp_free_ptr(t_size); + tcg_temp_free_ptr(t_ptr); + return; + } + t_desc = tcg_const_i32(simd_desc(oprsz, maxsz, 0)); if (vece == MO_64) { diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index cc6ec0f8c1..fb41bb7144 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 diff --git a/trace/control.c b/trace/control.c index b82fb87316..cd04dd4e0c 100644 --- a/trace/control.c +++ b/trace/control.c @@ -125,18 +125,18 @@ TraceEvent *trace_event_iter_next(TraceEventIter *iter) return NULL; } -void trace_list_events(void) +void trace_list_events(FILE *f) { TraceEventIter iter; TraceEvent *ev; trace_event_iter_init(&iter, NULL); while ((ev = trace_event_iter_next(&iter)) != NULL) { - fprintf(stderr, "%s\n", trace_event_get_name(ev)); + fprintf(f, "%s\n", trace_event_get_name(ev)); } #ifdef CONFIG_TRACE_DTRACE - fprintf(stderr, "This list of names of trace points may be incomplete " - "when using the DTrace/SystemTap backends.\n" - "Run 'qemu-trace-stap list %s' to print the full list.\n", + fprintf(f, "This list of names of trace points may be incomplete " + "when using the DTrace/SystemTap backends.\n" + "Run 'qemu-trace-stap list %s' to print the full list.\n", error_get_progname()); #endif } @@ -176,7 +176,7 @@ static void do_trace_enable_events(const char *line_buf) void trace_enable_events(const char *line_buf) { if (is_help_option(line_buf)) { - trace_list_events(); + trace_list_events(stdout); if (monitor_cur() == NULL) { exit(0); } diff --git a/trace/control.h b/trace/control.h index 05b95ea453..9522a7b318 100644 --- a/trace/control.h +++ b/trace/control.h @@ -201,10 +201,11 @@ void trace_fini_vcpu(CPUState *vcpu); /** * trace_list_events: + * @f: Where to send output. * * List all available events. */ -void trace_list_events(void); +void trace_list_events(FILE *f); /** * trace_enable_events: diff --git a/trace/meson.build b/trace/meson.build index b19309b327..a0be8f9b0d 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,3 +1,4 @@ + specific_ss.add(files('control-target.c')) trace_events_files = [] @@ -11,20 +12,17 @@ foreach dir : [ '.' ] + trace_events_subdirs trace_h = custom_target(fmt.format('trace', 'h'), output: fmt.format('trace', 'h'), input: trace_events_file, - command: [ tracetool, group, '--format=h', '@INPUT@' ], - capture: true) + command: [ tracetool, group, '--format=h', '@INPUT@', '@OUTPUT@' ]) genh += trace_h trace_c = custom_target(fmt.format('trace', 'c'), output: fmt.format('trace', 'c'), input: trace_events_file, - command: [ tracetool, group, '--format=c', '@INPUT@' ], - capture: true) + command: [ tracetool, group, '--format=c', '@INPUT@', '@OUTPUT@' ]) if 'CONFIG_TRACE_UST' in config_host trace_ust_h = custom_target(fmt.format('trace-ust', 'h'), output: fmt.format('trace-ust', 'h'), input: trace_events_file, - command: [ tracetool, group, '--format=ust-events-h', '@INPUT@' ], - capture: true) + command: [ tracetool, group, '--format=ust-events-h', '@INPUT@', '@OUTPUT@' ]) trace_ss.add(trace_ust_h, lttng, urcubp) genh += trace_ust_h endif @@ -33,8 +31,7 @@ foreach dir : [ '.' ] + trace_events_subdirs trace_dtrace = custom_target(fmt.format('trace-dtrace', 'dtrace'), output: fmt.format('trace-dtrace', 'dtrace'), input: trace_events_file, - command: [ tracetool, group, '--format=d', '@INPUT@' ], - capture: true) + command: [ tracetool, group, '--format=d', '@INPUT@', '@OUTPUT@' ]) trace_dtrace_h = custom_target(fmt.format('trace-dtrace', 'h'), output: fmt.format('trace-dtrace', 'h'), input: trace_dtrace, @@ -69,8 +66,7 @@ foreach d : [ gen = custom_target(d[0], output: d[0], input: meson.source_root() / 'trace-events', - command: [ tracetool, '--group=root', '--format=@0@'.format(d[1]), '@INPUT@' ], - capture: true) + command: [ tracetool, '--group=root', '--format=@0@'.format(d[1]), '@INPUT@', '@OUTPUT@' ]) specific_ss.add(when: 'CONFIG_TCG', if_true: gen) endforeach @@ -78,13 +74,11 @@ if 'CONFIG_TRACE_UST' in config_host trace_ust_all_h = custom_target('trace-ust-all.h', output: 'trace-ust-all.h', input: trace_events_files, - command: [ tracetool, '--group=all', '--format=ust-events-h', '@INPUT@' ], - capture: true) + command: [ tracetool, '--group=all', '--format=ust-events-h', '@INPUT@', '@OUTPUT@' ]) trace_ust_all_c = custom_target('trace-ust-all.c', output: 'trace-ust-all.c', input: trace_events_files, - command: [ tracetool, '--group=all', '--format=ust-events-c', '@INPUT@' ], - capture: true) + command: [ tracetool, '--group=all', '--format=ust-events-c', '@INPUT@', '@OUTPUT@' ]) trace_ss.add(trace_ust_all_h, trace_ust_all_c) genh += trace_ust_all_h endif diff --git a/util/readline.c b/util/readline.c index e534460da6..f1ac6e4769 100644 --- a/util/readline.c +++ b/util/readline.c @@ -240,6 +240,9 @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) } if (strcmp(hist_entry, cmdline) == 0) { same_entry: + if (idx == READLINE_MAX_CMDS - 1) { + return; + } new_entry = hist_entry; /* Put this entry at the end of history */ memmove(&rs->history[idx], &rs->history[idx + 1], |