diff options
author | Fathi Boudra <fathi.boudra@linaro.org> | 2012-12-25 16:58:44 +0200 |
---|---|---|
committer | Fathi Boudra <fathi.boudra@linaro.org> | 2012-12-25 16:58:44 +0200 |
commit | dd55a01d2af35f5e9dabb2b745770afd008c9e24 (patch) | |
tree | 2bcab272d5cd94aab77f89220f1b8a3bff3109ac /hw | |
parent | 07d16a97127c9f56263ed5d8c697ff98a748e480 (diff) |
Imported Upstream version 1.2.0-2012.09upstream/1.2.0-2012.09
Diffstat (limited to 'hw')
104 files changed, 2761 insertions, 862 deletions
diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 5c6ef2f..f277df2 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -65,6 +65,34 @@ hw-obj-$(CONFIG_XILINX) += xilinx_timer.o hw-obj-$(CONFIG_XILINX) += xilinx_uartlite.o hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o +hw-obj-$(CONFIG_XILINX_AXI) += stream.o + +# PKUnity SoC devices +hw-obj-$(CONFIG_PUV3) += puv3_intc.o +hw-obj-$(CONFIG_PUV3) += puv3_ost.o +hw-obj-$(CONFIG_PUV3) += puv3_gpio.o +hw-obj-$(CONFIG_PUV3) += puv3_pm.o +hw-obj-$(CONFIG_PUV3) += puv3_dma.o + +# ARM devices +hw-obj-$(CONFIG_ARM_TIMER) += arm_timer.o +hw-obj-$(CONFIG_PL011) += pl011.o +hw-obj-$(CONFIG_PL022) += pl022.o +hw-obj-$(CONFIG_PL031) += pl031.o +hw-obj-$(CONFIG_PL041) += pl041.o lm4549.o +hw-obj-$(CONFIG_PL050) += pl050.o +hw-obj-$(CONFIG_PL061) += pl061.o +hw-obj-$(CONFIG_PL080) += pl080.o +hw-obj-$(CONFIG_PL110) += pl110.o +hw-obj-$(CONFIG_PL181) += pl181.o +hw-obj-$(CONFIG_PL190) += pl190.o +hw-obj-$(CONFIG_PL310) += arm_l2x0.o +hw-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o +hw-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o +hw-obj-$(CONFIG_CADENCE) += cadence_uart.o +hw-obj-$(CONFIG_CADENCE) += cadence_ttc.o +hw-obj-$(CONFIG_CADENCE) += cadence_gem.o +hw-obj-$(CONFIG_XGMAC) += xgmac.o # PCI watchdog devices hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o @@ -106,6 +134,8 @@ hw-obj-$(CONFIG_DP8393X) += dp8393x.o hw-obj-$(CONFIG_DS1225Y) += ds1225y.o hw-obj-$(CONFIG_MIPSNET) += mipsnet.o +hw-obj-y += null-machine.o + # Sound sound-obj-y = sound-obj-$(CONFIG_SB16) += sb16.o @@ -22,6 +22,7 @@ #include "hw.h" #include "pc.h" #include "acpi.h" +#include "monitor.h" struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ @@ -386,6 +387,7 @@ void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val, char s4) break; default: if (sus_typ == s4) { /* S4 request */ + monitor_protocol_event(QEVENT_SUSPEND_DISK, NULL); qemu_system_shutdown_request(); } break; diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 0aace60..c56220b 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -305,7 +305,6 @@ static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots) if (pc->no_hotplug) { slot_free = false; } else { - object_unparent(OBJECT(dev)); qdev_free(qdev); } } @@ -353,6 +352,9 @@ static void piix4_reset(void *opaque) pci_conf[0x5a] = 0; pci_conf[0x5b] = 0; + pci_conf[0x40] = 0x01; /* PM io base read only bit */ + pci_conf[0x80] = 0; + if (s->kvm_enabled) { /* Mark SMM as already inited (until KVM supports SMM). */ pci_conf[0x5B] = 0x02; @@ -392,8 +394,6 @@ static int piix4_pm_initfn(PCIDevice *dev) pci_conf[0x09] = 0x00; pci_conf[0x3d] = 0x01; // interrupt pin 1 - pci_conf[0x40] = 0x01; /* PM io base read only bit */ - /* APM */ apm_init(&s->apm, apm_ctrl_changed, s); diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c index 6735577..ea546f8 100644 --- a/hw/alpha_pci.c +++ b/hw/alpha_pci.c @@ -11,6 +11,7 @@ #include "qemu-log.h" #include "sysemu.h" #include "vmware_vga.h" +#include "vga-pci.h" /* PCI IO reads/writes, to byte-word addressable memory. */ diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c index 872e112..b7cf4e2 100644 --- a/hw/alpha_typhoon.c +++ b/hw/alpha_typhoon.c @@ -15,6 +15,8 @@ #include "exec-memory.h" +#define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" + typedef struct TyphoonCchip { MemoryRegion region; uint64_t misc; @@ -40,8 +42,12 @@ typedef struct TyphoonPchip { TyphoonWindow win[4]; } TyphoonPchip; +#define TYPHOON_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(TyphoonState, (obj), TYPE_TYPHOON_PCI_HOST_BRIDGE) + typedef struct TyphoonState { - PCIHostState host; + PCIHostState parent_obj; + TyphoonCchip cchip; TyphoonPchip pchip; MemoryRegion dchip_region; @@ -700,16 +706,16 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, MemoryRegion *addr_space = get_system_memory(); MemoryRegion *addr_space_io = get_system_io(); DeviceState *dev; - PCIHostState *p; TyphoonState *s; + PCIHostState *phb; PCIBus *b; int i; - dev = qdev_create(NULL, "typhoon-pcihost"); + dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE); qdev_init_nofail(dev); - p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); - s = container_of(p, TyphoonState, host); + s = TYPHOON_PCI_HOST_BRIDGE(dev); + phb = PCI_HOST_BRIDGE(dev); /* Remember the CPUs so that we can deliver interrupts to them. */ for (i = 0; i < 4; i++) { @@ -763,10 +769,10 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, memory_region_add_subregion(addr_space, 0x801fc000000ULL, &s->pchip.reg_io); - b = pci_register_bus(&s->host.busdev.qdev, "pci", + b = pci_register_bus(dev, "pci", typhoon_set_irq, sys_map_irq, s, &s->pchip.reg_mem, addr_space_io, 0, 64); - s->host.bus = b; + phb->bus = b; /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b, @@ -817,9 +823,9 @@ static void typhoon_pcihost_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo typhoon_pcihost_info = { - .name = "typhoon-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo typhoon_pcihost_info = { + .name = TYPE_TYPHOON_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(TyphoonState), .class_init = typhoon_pcihost_class_init, }; diff --git a/hw/apic_common.c b/hw/apic_common.c index 58e63b0..371f95d 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -299,7 +299,9 @@ static int apic_init_common(SysBusDevice *dev) sysbus_init_mmio(dev, &s->io_memory); - if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK) { + /* Note: We need at least 1M to map the VAPIC option ROM */ + if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK && + ram_size >= 1024 * 1024) { vapic = sysbus_create_simple("kvmvapic", -1, NULL); } s->vapic = vapic; diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 01ab3aa..87ca004 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,10 +1,5 @@ -obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o -obj-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o -obj-y += versatile_pci.o -obj-y += versatile_i2c.o -obj-y += cadence_uart.o -obj-y += cadence_ttc.o -obj-y += cadence_gem.o +obj-y = integratorcp.o versatilepb.o arm_pic.o +obj-y += arm_boot.o obj-y += xilinx_zynq.o zynq_slcr.o obj-y += arm_gic.o arm_gic_common.o obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o @@ -12,12 +7,9 @@ obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o obj-y += exynos4210_rtc.o exynos4210_i2c.o -obj-y += arm_l2x0.o obj-y += arm_mptimer.o a15mpcore.o -obj-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o +obj-y += armv7m.o armv7m_nvic.o stellaris.o stellaris_enet.o obj-y += highbank.o -obj-y += pl061.o -obj-y += xgmac.o obj-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-y += gumstix.o @@ -39,7 +31,6 @@ obj-y += strongarm.o obj-y += collie.o obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o obj-y += kzm.o -obj-y += pl041.o lm4549.o obj-$(CONFIG_FDT) += ../device_tree.o obj-$(CONFIG_KVM) += kvm/arm_gic.o diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 186ac66..55871fa 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -703,6 +703,7 @@ static TypeInfo arm_gic_info = { .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(gic_state), .class_init = arm_gic_class_init, + .class_size = sizeof(ARMGICClass), }; static void arm_gic_register_types(void) diff --git a/hw/armv7m.c b/hw/armv7m.c index 8cec78d..9f66667 100644 --- a/hw/armv7m.c +++ b/hw/armv7m.c @@ -227,6 +227,11 @@ qemu_irq *armv7m_init(MemoryRegion *address_space_mem, big_endian = 0; #endif + if (!kernel_filename) { + fprintf(stderr, "Guest image must be specified (using -kernel)\n"); + exit(1); + } + image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, NULL, big_endian, ELF_MACHINE, 1); if (image_size < 0) { diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 4867c1d..6a0832e 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -467,7 +467,7 @@ static int armv7m_nvic_init(SysBusDevice *dev) s->gic.num_cpu = 1; /* Tell the common code we're an NVIC */ s->gic.revision = 0xffffffff; - s->gic.num_irq = s->num_irq; + s->num_irq = s->gic.num_irq; nc->parent_init(dev); gic_init_irqs_and_distributor(&s->gic, s->num_irq); /* The NVIC and system controller register area looks like this: @@ -498,14 +498,21 @@ static int armv7m_nvic_init(SysBusDevice *dev) return 0; } -static Property armv7m_nvic_properties[] = { +static void armv7m_nvic_instance_init(Object *obj) +{ + /* We have a different default value for the num-irq property + * than our superclass. This function runs after qdev init + * has set the defaults from the Property array and before + * any user-specified property setting, so just modify the + * value in the gic_state struct. + */ + gic_state *s = ARM_GIC_COMMON(obj); /* The ARM v7m may have anything from 0 to 496 external interrupt * IRQ lines. We default to 64. Other boards may differ and should - * set this property appropriately. + * set the num-irq property appropriately. */ - DEFINE_PROP_UINT32("num-irq", nvic_state, num_irq, 64), - DEFINE_PROP_END_OF_LIST(), -}; + s->num_irq = 64; +} static void armv7m_nvic_class_init(ObjectClass *klass, void *data) { @@ -518,12 +525,12 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) sdc->init = armv7m_nvic_init; dc->vmsd = &vmstate_nvic; dc->reset = armv7m_nvic_reset; - dc->props = armv7m_nvic_properties; } static TypeInfo armv7m_nvic_info = { .name = TYPE_NVIC, .parent = TYPE_ARM_GIC_COMMON, + .instance_init = armv7m_nvic_instance_init, .instance_size = sizeof(nvic_state), .class_init = armv7m_nvic_class_init, .class_size = sizeof(NVICClass), diff --git a/hw/boards.h b/hw/boards.h index 59c01d0..a2e0a54 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -12,11 +12,14 @@ typedef void QEMUMachineInitFunc(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model); +typedef void QEMUMachineResetFunc(void); + typedef struct QEMUMachine { const char *name; const char *alias; const char *desc; QEMUMachineInitFunc *init; + QEMUMachineResetFunc *reset; int use_scsi; int max_cpus; unsigned int no_serial:1, diff --git a/hw/bonito.c b/hw/bonito.c index 77786f8..6084ac4 100644 --- a/hw/bonito.c +++ b/hw/bonito.c @@ -180,11 +180,14 @@ #define PCI_ADDR(busno,devno,funno,regno) \ ((((busno)<<16)&0xff0000) + (((devno)<<11)&0xf800) + (((funno)<<8)&0x700) + (regno)) -typedef PCIHostState BonitoState; +#define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost" + +typedef struct BonitoState BonitoState; typedef struct PCIBonitoState { PCIDevice dev; + BonitoState *pcihost; uint32_t regs[BONITO_REGS]; @@ -218,7 +221,16 @@ typedef struct PCIBonitoState } PCIBonitoState; -PCIBonitoState * bonito_state; +#define BONITO_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(BonitoState, (obj), TYPE_BONITO_PCI_HOST_BRIDGE) + +struct BonitoState { + PCIHostState parent_obj; + + qemu_irq *pic; + + PCIBonitoState *pci_dev; +}; static void bonito_writel(void *opaque, target_phys_addr_t addr, uint64_t val, unsigned size) @@ -314,9 +326,10 @@ static void bonito_pciconf_writel(void *opaque, target_phys_addr_t addr, uint64_t val, unsigned size) { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); DPRINTF("bonito_pciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); - s->dev.config_write(&s->dev, addr, val, 4); + d->config_write(d, addr, val, 4); } static uint64_t bonito_pciconf_readl(void *opaque, target_phys_addr_t addr, @@ -324,9 +337,10 @@ static uint64_t bonito_pciconf_readl(void *opaque, target_phys_addr_t addr, { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); DPRINTF("bonito_pciconf_readl "TARGET_FMT_plx"\n", addr); - return s->dev.config_read(&s->dev, addr, 4); + return d->config_read(d, addr, 4); } /* north bridge PCI configure space. 0x1fe0 0000 - 0x1fe0 00ff */ @@ -402,6 +416,7 @@ static const MemoryRegionOps bonito_cop_ops = { static uint32_t bonito_sbridge_pciaddr(void *opaque, target_phys_addr_t addr) { PCIBonitoState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); uint32_t cfgaddr; uint32_t idsel; uint32_t devno; @@ -423,13 +438,13 @@ static uint32_t bonito_sbridge_pciaddr(void *opaque, target_phys_addr_t addr) regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET; if (idsel == 0) { - fprintf(stderr, "error in bonito pci config address" TARGET_FMT_plx + fprintf(stderr, "error in bonito pci config address " TARGET_FMT_plx ",pcimap_cfg=%x\n", addr, s->regs[BONITO_PCIMAP_CFG]); exit(1); } - pciaddr = PCI_ADDR(pci_bus_num(s->pcihost->bus), devno, funno, regno); + pciaddr = PCI_ADDR(pci_bus_num(phb->bus), devno, funno, regno); DPRINTF("cfgaddr %x pciaddr %x busno %x devno %d funno %d regno %d\n", - cfgaddr, pciaddr, pci_bus_num(s->pcihost->bus), devno, funno, regno); + cfgaddr, pciaddr, pci_bus_num(phb->bus), devno, funno, regno); return pciaddr; } @@ -438,6 +453,8 @@ static void bonito_spciconf_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); uint32_t pciaddr; uint16_t status; @@ -449,24 +466,26 @@ static void bonito_spciconf_writeb(void *opaque, target_phys_addr_t addr, } /* set the pci address in s->config_reg */ - s->pcihost->config_reg = (pciaddr) | (1u << 31); - pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val & 0xff, 1); + phb->config_reg = (pciaddr) | (1u << 31); + pci_data_write(phb->bus, phb->config_reg, val & 0xff, 1); /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(s->dev.config + PCI_STATUS); + status = pci_get_word(d->config + PCI_STATUS); status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(s->dev.config + PCI_STATUS, status); + pci_set_word(d->config + PCI_STATUS, status); } static void bonito_spciconf_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); uint32_t pciaddr; uint16_t status; DPRINTF("bonito_spciconf_writew "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr&0x1)==0); + assert((addr & 0x1) == 0); pciaddr = bonito_sbridge_pciaddr(s, addr); @@ -475,24 +494,26 @@ static void bonito_spciconf_writew(void *opaque, target_phys_addr_t addr, } /* set the pci address in s->config_reg */ - s->pcihost->config_reg = (pciaddr) | (1u << 31); - pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val, 2); + phb->config_reg = (pciaddr) | (1u << 31); + pci_data_write(phb->bus, phb->config_reg, val, 2); /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(s->dev.config + PCI_STATUS); + status = pci_get_word(d->config + PCI_STATUS); status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(s->dev.config + PCI_STATUS, status); + pci_set_word(d->config + PCI_STATUS, status); } static void bonito_spciconf_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); uint32_t pciaddr; uint16_t status; DPRINTF("bonito_spciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr&0x3)==0); + assert((addr & 0x3) == 0); pciaddr = bonito_sbridge_pciaddr(s, addr); @@ -501,18 +522,20 @@ static void bonito_spciconf_writel(void *opaque, target_phys_addr_t addr, } /* set the pci address in s->config_reg */ - s->pcihost->config_reg = (pciaddr) | (1u << 31); - pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val, 4); + phb->config_reg = (pciaddr) | (1u << 31); + pci_data_write(phb->bus, phb->config_reg, val, 4); /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(s->dev.config + PCI_STATUS); + status = pci_get_word(d->config + PCI_STATUS); status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(s->dev.config + PCI_STATUS, status); + pci_set_word(d->config + PCI_STATUS, status); } static uint32_t bonito_spciconf_readb(void *opaque, target_phys_addr_t addr) { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); uint32_t pciaddr; uint16_t status; @@ -524,24 +547,26 @@ static uint32_t bonito_spciconf_readb(void *opaque, target_phys_addr_t addr) } /* set the pci address in s->config_reg */ - s->pcihost->config_reg = (pciaddr) | (1u << 31); + phb->config_reg = (pciaddr) | (1u << 31); /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(s->dev.config + PCI_STATUS); + status = pci_get_word(d->config + PCI_STATUS); status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(s->dev.config + PCI_STATUS, status); + pci_set_word(d->config + PCI_STATUS, status); - return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 1); + return pci_data_read(phb->bus, phb->config_reg, 1); } static uint32_t bonito_spciconf_readw(void *opaque, target_phys_addr_t addr) { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); uint32_t pciaddr; uint16_t status; DPRINTF("bonito_spciconf_readw "TARGET_FMT_plx"\n", addr); - assert((addr&0x1)==0); + assert((addr & 0x1) == 0); pciaddr = bonito_sbridge_pciaddr(s, addr); @@ -550,24 +575,26 @@ static uint32_t bonito_spciconf_readw(void *opaque, target_phys_addr_t addr) } /* set the pci address in s->config_reg */ - s->pcihost->config_reg = (pciaddr) | (1u << 31); + phb->config_reg = (pciaddr) | (1u << 31); /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(s->dev.config + PCI_STATUS); + status = pci_get_word(d->config + PCI_STATUS); status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(s->dev.config + PCI_STATUS, status); + pci_set_word(d->config + PCI_STATUS, status); - return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 2); + return pci_data_read(phb->bus, phb->config_reg, 2); } static uint32_t bonito_spciconf_readl(void *opaque, target_phys_addr_t addr) { PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); uint32_t pciaddr; uint16_t status; DPRINTF("bonito_spciconf_readl "TARGET_FMT_plx"\n", addr); - assert((addr&0x3) == 0); + assert((addr & 0x3) == 0); pciaddr = bonito_sbridge_pciaddr(s, addr); @@ -576,14 +603,14 @@ static uint32_t bonito_spciconf_readl(void *opaque, target_phys_addr_t addr) } /* set the pci address in s->config_reg */ - s->pcihost->config_reg = (pciaddr) | (1u << 31); + phb->config_reg = (pciaddr) | (1u << 31); /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(s->dev.config + PCI_STATUS); + status = pci_get_word(d->config + PCI_STATUS); status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(s->dev.config + PCI_STATUS, status); + pci_set_word(d->config + PCI_STATUS, status); - return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 4); + return pci_data_read(phb->bus, phb->config_reg, 4); } /* south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */ @@ -607,13 +634,15 @@ static const MemoryRegionOps bonito_spciconf_ops = { static void pci_bonito_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pic = opaque; + BonitoState *s = opaque; + qemu_irq *pic = s->pic; + PCIBonitoState *bonito_state = s->pci_dev; int internal_irq = irq_num - BONITO_IRQ_BASE; - if (bonito_state->regs[BONITO_INTEDGE] & (1<<internal_irq)) { + if (bonito_state->regs[BONITO_INTEDGE] & (1 << internal_irq)) { qemu_irq_pulse(*pic); } else { /* level triggered */ - if (bonito_state->regs[BONITO_INTPOL] & (1<<internal_irq)) { + if (bonito_state->regs[BONITO_INTPOL] & (1 << internal_irq)) { qemu_irq_raise(*pic); } else { qemu_irq_lower(*pic); @@ -673,13 +702,21 @@ static const VMStateDescription vmstate_bonito = { static int bonito_pcihost_initfn(SysBusDevice *dev) { + PCIHostState *phb = PCI_HOST_BRIDGE(dev); + + phb->bus = pci_register_bus(DEVICE(dev), "pci", + pci_bonito_set_irq, pci_bonito_map_irq, dev, + get_system_memory(), get_system_io(), + 0x28, 32); + return 0; } static int bonito_initfn(PCIDevice *dev) { PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev); - SysBusDevice *sysbus = &s->pcihost->busdev; + SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */ pci_config_set_prog_interface(dev->config, 0x00); @@ -691,15 +728,15 @@ static int bonito_initfn(PCIDevice *dev) sysbus_mmio_map(sysbus, 0, BONITO_INTERNAL_REG_BASE); /* set the north bridge pci configure mapping */ - memory_region_init_io(&s->pcihost->conf_mem, &bonito_pciconf_ops, s, + memory_region_init_io(&phb->conf_mem, &bonito_pciconf_ops, s, "north-bridge-pci-config", BONITO_PCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &s->pcihost->conf_mem); + sysbus_init_mmio(sysbus, &phb->conf_mem); sysbus_mmio_map(sysbus, 1, BONITO_PCICONFIG_BASE); /* set the south bridge pci configure mapping */ - memory_region_init_io(&s->pcihost->data_mem, &bonito_spciconf_ops, s, + memory_region_init_io(&phb->data_mem, &bonito_spciconf_ops, s, "south-bridge-pci-config", BONITO_SPCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &s->pcihost->data_mem); + sysbus_init_mmio(sysbus, &phb->data_mem); sysbus_mmio_map(sysbus, 2, BONITO_SPCICONFIG_BASE); memory_region_init_io(&s->iomem_ldma, &bonito_ldma_ops, s, @@ -742,28 +779,25 @@ static int bonito_initfn(PCIDevice *dev) PCIBus *bonito_init(qemu_irq *pic) { DeviceState *dev; - PCIBus *b; BonitoState *pcihost; + PCIHostState *phb; PCIBonitoState *s; PCIDevice *d; - dev = qdev_create(NULL, "Bonito-pcihost"); - pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev)); - b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq, - pci_bonito_map_irq, pic, get_system_memory(), - get_system_io(), - 0x28, 32); - pcihost->bus = b; + dev = qdev_create(NULL, TYPE_BONITO_PCI_HOST_BRIDGE); + phb = PCI_HOST_BRIDGE(dev); + pcihost = BONITO_PCI_HOST_BRIDGE(dev); + pcihost->pic = pic; qdev_init_nofail(dev); /* set the pcihost pointer before bonito_initfn is called */ - d = pci_create(b, PCI_DEVFN(0, 0), "Bonito"); + d = pci_create(phb->bus, PCI_DEVFN(0, 0), "Bonito"); s = DO_UPCAST(PCIBonitoState, dev, d); s->pcihost = pcihost; - bonito_state = s; - qdev_init_nofail(&d->qdev); + pcihost->pci_dev = s; + qdev_init_nofail(DEVICE(d)); - return b; + return phb->bus; } static void bonito_class_init(ObjectClass *klass, void *data) @@ -781,7 +815,7 @@ static void bonito_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_bonito; } -static TypeInfo bonito_info = { +static const TypeInfo bonito_info = { .name = "Bonito", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBonitoState), @@ -797,9 +831,9 @@ static void bonito_pcihost_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo bonito_pcihost_info = { - .name = "Bonito-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo bonito_pcihost_info = { + .name = TYPE_BONITO_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(BonitoState), .class_init = bonito_pcihost_class_init, }; diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 623dd68..e8dcc6b 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -27,8 +27,8 @@ * available at http://home.worldonline.dk/~finth/ */ #include "hw.h" -#include "pc.h" #include "pci.h" +#include "vga-pci.h" #include "console.h" #include "vga_int.h" #include "loader.h" diff --git a/hw/dec_pci.c b/hw/dec_pci.c index 37337bf..c30ade3 100644 --- a/hw/dec_pci.c +++ b/hw/dec_pci.c @@ -40,9 +40,10 @@ #define DEC_DPRINTF(fmt, ...) #endif +#define DEC_21154(obj) OBJECT_CHECK(DECState, (obj), TYPE_DEC_21154) + typedef struct DECState { - SysBusDevice busdev; - PCIHostState host_state; + PCIHostState parent_obj; } DECState; static int dec_map_irq(PCIDevice *pci_dev, int irq_num) @@ -66,7 +67,7 @@ static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pci_device; } -static TypeInfo dec_21154_pci_bridge_info = { +static const TypeInfo dec_21154_pci_bridge_info = { .name = "dec-21154-p2p-bridge", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBridge), @@ -88,16 +89,16 @@ PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) static int pci_dec_21154_device_init(SysBusDevice *dev) { - DECState *s; + PCIHostState *phb; - s = FROM_SYSBUS(DECState, dev); + phb = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&s->host_state.conf_mem, &pci_host_conf_le_ops, - &s->host_state, "pci-conf-idx", 0x1000); - memory_region_init_io(&s->host_state.data_mem, &pci_host_data_le_ops, - &s->host_state, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &s->host_state.conf_mem); - sysbus_init_mmio(dev, &s->host_state.data_mem); + memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops, + dev, "pci-data-idx", 0x1000); + sysbus_init_mmio(dev, &phb->conf_mem); + sysbus_init_mmio(dev, &phb->data_mem); return 0; } @@ -119,7 +120,7 @@ static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) k->is_bridge = 1; } -static TypeInfo dec_21154_pci_host_info = { +static const TypeInfo dec_21154_pci_host_info = { .name = "dec-21154", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -133,9 +134,9 @@ static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data) sdc->init = pci_dec_21154_device_init; } -static TypeInfo pci_dec_21154_device_info = { - .name = "dec-21154-sysbus", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo pci_dec_21154_device_info = { + .name = TYPE_DEC_21154, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(DECState), .class_init = pci_dec_21154_device_class_init, }; diff --git a/hw/dec_pci.h b/hw/dec_pci.h index 79264ba..17dc0c2 100644 --- a/hw/dec_pci.h +++ b/hw/dec_pci.h @@ -3,6 +3,8 @@ #include "qemu-common.h" +#define TYPE_DEC_21154 "dec-21154-sysbus" + PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn); #endif @@ -87,7 +87,9 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) target = s->wregs[ESP_WBUSID] & BUSID_DID; if (s->dma) { - dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8); + dmalen = s->rregs[ESP_TCLO]; + dmalen |= s->rregs[ESP_TCMID] << 8; + dmalen |= s->rregs[ESP_TCHI] << 16; s->dma_memory_read(s->dma_opaque, buf, dmalen); } else { dmalen = s->ti_size; @@ -226,6 +228,7 @@ static void esp_dma_done(ESPState *s) s->rregs[ESP_RFLAGS] = 0; s->rregs[ESP_TCLO] = 0; s->rregs[ESP_TCMID] = 0; + s->rregs[ESP_TCHI] = 0; esp_raise_irq(s); } @@ -328,7 +331,9 @@ static void handle_ti(ESPState *s) return; } - dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8); + dmalen = s->rregs[ESP_TCLO]; + dmalen |= s->rregs[ESP_TCMID] << 8; + dmalen |= s->rregs[ESP_TCHI] << 16; if (dmalen==0) { dmalen=0x10000; } @@ -429,6 +434,7 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) switch (saddr) { case ESP_TCLO: case ESP_TCMID: + case ESP_TCHI: s->rregs[ESP_RSTAT] &= ~STAT_TC; break; case ESP_FIFO: @@ -448,6 +454,7 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) /* Reload DMA counter. */ s->rregs[ESP_TCLO] = s->wregs[ESP_TCLO]; s->rregs[ESP_TCMID] = s->wregs[ESP_TCMID]; + s->rregs[ESP_TCHI] = s->wregs[ESP_TCHI]; } else { s->dma = 0; } @@ -530,13 +537,12 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) case ESP_WBUSID ... ESP_WSYNO: break; case ESP_CFG1: + case ESP_CFG2: case ESP_CFG3: + case ESP_RES3: case ESP_RES4: s->rregs[saddr] = val; break; case ESP_WCCF ... ESP_WTEST: break; - case ESP_CFG2 ... ESP_RES4: - s->rregs[saddr] = val; - break; default: trace_esp_error_invalid_write(val, saddr); return; diff --git a/hw/framebuffer.c b/hw/framebuffer.c index f4747cd..85a00a5 100644 --- a/hw/framebuffer.c +++ b/hw/framebuffer.c @@ -28,7 +28,7 @@ void framebuffer_update_display( MemoryRegion *address_space, target_phys_addr_t base, int cols, /* Width in pixels. */ - int rows, /* Leight in pixels. */ + int rows, /* Height in pixels. */ int src_width, /* Length of source line, in bytes. */ int dest_row_pitch, /* Bytes between adjacent horizontal output pixels. */ int dest_col_pitch, /* Bytes between adjacent vertical output pixels. */ diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c index 81ff3a3..67da307 100644 --- a/hw/grackle_pci.c +++ b/hw/grackle_pci.c @@ -23,10 +23,9 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "pci_host.h" #include "ppc_mac.h" #include "pci.h" -#include "pci_host.h" /* debug Grackle */ //#define DEBUG_GRACKLE @@ -38,9 +37,12 @@ #define GRACKLE_DPRINTF(fmt, ...) #endif +#define GRACKLE_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(GrackleState, (obj), TYPE_GRACKLE_PCI_HOST_BRIDGE) + typedef struct GrackleState { - SysBusDevice busdev; - PCIHostState host_state; + PCIHostState parent_obj; + MemoryRegion pci_mmio; MemoryRegion pci_hole; } GrackleState; @@ -59,22 +61,20 @@ static void pci_grackle_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(pic[irq_num + 0x15], level); } -static void pci_grackle_reset(void *opaque) -{ -} - PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io) { DeviceState *dev; SysBusDevice *s; + PCIHostState *phb; GrackleState *d; - dev = qdev_create(NULL, "grackle-pcihost"); + dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); - d = FROM_SYSBUS(GrackleState, s); + s = SYS_BUS_DEVICE(dev); + phb = PCI_HOST_BRIDGE(dev); + d = GRACKLE_PCI_HOST_BRIDGE(dev); memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, @@ -82,36 +82,35 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", - pci_grackle_set_irq, - pci_grackle_map_irq, - pic, - &d->pci_mmio, - address_space_io, - 0, 4); + phb->bus = pci_register_bus(dev, "pci", + pci_grackle_set_irq, + pci_grackle_map_irq, + pic, + &d->pci_mmio, + address_space_io, + 0, 4); - pci_create_simple(d->host_state.bus, 0, "grackle"); + pci_create_simple(phb->bus, 0, "grackle"); sysbus_mmio_map(s, 0, base); sysbus_mmio_map(s, 1, base + 0x00200000); - return d->host_state.bus; + return phb->bus; } static int pci_grackle_init_device(SysBusDevice *dev) { - GrackleState *s; + PCIHostState *phb; - s = FROM_SYSBUS(GrackleState, dev); + phb = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&s->host_state.conf_mem, &pci_host_conf_le_ops, - &s->host_state, "pci-conf-idx", 0x1000); - memory_region_init_io(&s->host_state.data_mem, &pci_host_data_le_ops, - &s->host_state, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &s->host_state.conf_mem); - sysbus_init_mmio(dev, &s->host_state.data_mem); + memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops, + dev, "pci-data-idx", 0x1000); + sysbus_init_mmio(dev, &phb->conf_mem); + sysbus_init_mmio(dev, &phb->data_mem); - qemu_register_reset(pci_grackle_reset, &s->host_state); return 0; } @@ -134,7 +133,7 @@ static void grackle_pci_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo grackle_pci_info = { +static const TypeInfo grackle_pci_info = { .name = "grackle", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -150,9 +149,9 @@ static void pci_grackle_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo grackle_pci_host_info = { - .name = "grackle-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo grackle_pci_host_info = { + .name = TYPE_GRACKLE_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(GrackleState), .class_init = pci_grackle_class_init, }; diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c index a2d0e5a..e95e664 100644 --- a/hw/gt64xxx.c +++ b/hw/gt64xxx.c @@ -229,9 +229,14 @@ target_phys_addr_t regname ##_length; \ MemoryRegion regname ##_mem +#define TYPE_GT64120_PCI_HOST_BRIDGE "gt64120" + +#define GT64120_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(GT64120State, (obj), TYPE_GT64120_PCI_HOST_BRIDGE) + typedef struct GT64120State { - SysBusDevice busdev; - PCIHostState pci; + PCIHostState parent_obj; + uint32_t regs[GT_REGS]; PCI_MAPPING_ENTRY(PCI0IO); PCI_MAPPING_ENTRY(ISD); @@ -310,6 +315,7 @@ static void gt64120_writel (void *opaque, target_phys_addr_t addr, uint64_t val, unsigned size) { GT64120State *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); uint32_t saddr; if (!(s->regs[GT_CPU] & 0x00001000)) @@ -530,13 +536,15 @@ static void gt64120_writel (void *opaque, target_phys_addr_t addr, /* not implemented */ break; case GT_PCI0_CFGADDR: - s->pci.config_reg = val & 0x80fffffc; + phb->config_reg = val & 0x80fffffc; break; case GT_PCI0_CFGDATA: - if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800)) + if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { val = bswap32(val); - if (s->pci.config_reg & (1u << 31)) - pci_data_write(s->pci.bus, s->pci.config_reg, val, 4); + } + if (phb->config_reg & (1u << 31)) { + pci_data_write(phb->bus, phb->config_reg, val, 4); + } break; /* Interrupts */ @@ -589,6 +597,7 @@ static uint64_t gt64120_readl (void *opaque, target_phys_addr_t addr, unsigned size) { GT64120State *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); uint32_t val; uint32_t saddr; @@ -770,15 +779,17 @@ static uint64_t gt64120_readl (void *opaque, /* PCI Internal */ case GT_PCI0_CFGADDR: - val = s->pci.config_reg; + val = phb->config_reg; break; case GT_PCI0_CFGDATA: - if (!(s->pci.config_reg & (1 << 31))) + if (!(phb->config_reg & (1 << 31))) { val = 0xffffffff; - else - val = pci_data_read(s->pci.bus, s->pci.config_reg, 4); - if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800)) + } else { + val = pci_data_read(phb->bus, phb->config_reg, 4); + } + if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { val = bswap32(val); + } break; case GT_PCI0_CMD: @@ -1083,31 +1094,31 @@ static void gt64120_reset(void *opaque) PCIBus *gt64120_register(qemu_irq *pic) { - SysBusDevice *s; GT64120State *d; + PCIHostState *phb; DeviceState *dev; - dev = qdev_create(NULL, "gt64120"); + dev = qdev_create(NULL, TYPE_GT64120_PCI_HOST_BRIDGE); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); - d = FROM_SYSBUS(GT64120State, s); - d->pci.bus = pci_register_bus(&d->busdev.qdev, "pci", - gt64120_pci_set_irq, gt64120_pci_map_irq, - pic, - get_system_memory(), - get_system_io(), - PCI_DEVFN(18, 0), 4); + d = GT64120_PCI_HOST_BRIDGE(dev); + phb = PCI_HOST_BRIDGE(dev); + phb->bus = pci_register_bus(dev, "pci", + gt64120_pci_set_irq, gt64120_pci_map_irq, + pic, + get_system_memory(), + get_system_io(), + PCI_DEVFN(18, 0), 4); memory_region_init_io(&d->ISD_mem, &isd_mem_ops, d, "isd-mem", 0x1000); - pci_create_simple(d->pci.bus, PCI_DEVFN(0, 0), "gt64120_pci"); - return d->pci.bus; + pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); + return phb->bus; } static int gt64120_init(SysBusDevice *dev) { GT64120State *s; - s = FROM_SYSBUS(GT64120State, dev); + s = GT64120_PCI_HOST_BRIDGE(dev); /* FIXME: This value is computed from registers during reset, but some devices (e.g. VGA card) need to know it when they are registered. @@ -1147,7 +1158,7 @@ static void gt64120_pci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; } -static TypeInfo gt64120_pci_info = { +static const TypeInfo gt64120_pci_info = { .name = "gt64120_pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -1161,9 +1172,9 @@ static void gt64120_class_init(ObjectClass *klass, void *data) sdc->init = gt64120_init; } -static TypeInfo gt64120_info = { - .name = "gt64120", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo gt64120_info = { + .name = TYPE_GT64120_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(GT64120State), .class_init = gt64120_class_init, }; diff --git a/hw/i82378.c b/hw/i82378.c index 9b11d90..2123c14 100644 --- a/hw/i82378.c +++ b/hw/i82378.c @@ -225,7 +225,6 @@ static int pci_i82378_init(PCIDevice *dev) pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->io); memory_region_init_io(&s->mem, &i82378_mem_ops, s, "i82378-mem", 0x01000000); - memory_region_set_coalescing(&s->mem); pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); /* Make I/O address read only */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index efea93f..5ea3cad 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -636,7 +636,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) } } -static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) +static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset) { AHCICmdHdr *cmd = ad->cur_cmd; uint32_t opts = le32_to_cpu(cmd->opts); @@ -647,6 +647,10 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) uint8_t *prdt; int i; int r = 0; + int sum = 0; + int off_idx = -1; + int off_pos = -1; + int tbl_entry_size; if (!sglist_alloc_hint) { DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts); @@ -669,10 +673,31 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) /* Get entries in the PRDT, init a qemu sglist accordingly */ if (sglist_alloc_hint > 0) { AHCI_SG *tbl = (AHCI_SG *)prdt; - - qemu_sglist_init(sglist, sglist_alloc_hint, ad->hba->dma); + sum = 0; for (i = 0; i < sglist_alloc_hint; i++) { /* flags_size is zero-based */ + tbl_entry_size = (le32_to_cpu(tbl[i].flags_size) + 1); + if (offset <= (sum + tbl_entry_size)) { + off_idx = i; + off_pos = offset - sum; + break; + } + sum += tbl_entry_size; + } + if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) { + DPRINTF(ad->port_no, "%s: Incorrect offset! " + "off_idx: %d, off_pos: %d\n", + __func__, off_idx, off_pos); + r = -1; + goto out; + } + + qemu_sglist_init(sglist, (sglist_alloc_hint - off_idx), ad->hba->dma); + qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos), + le32_to_cpu(tbl[off_idx].flags_size) + 1 - off_pos); + + for (i = off_idx + 1; i < sglist_alloc_hint; i++) { + /* flags_size is zero-based */ qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), le32_to_cpu(tbl[i].flags_size) + 1); } @@ -745,7 +770,7 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2, s->dev[port].port.ifs[0].nb_sectors - 1); - ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist); + ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist, 0); ncq_tfs->tag = tag; switch(ncq_fis->command) { @@ -970,7 +995,7 @@ static int ahci_start_transfer(IDEDMA *dma) goto out; } - if (!ahci_populate_sglist(ad, &s->sg)) { + if (!ahci_populate_sglist(ad, &s->sg, 0)) { has_sglist = 1; } @@ -1015,6 +1040,7 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, DPRINTF(ad->port_no, "\n"); ad->dma_cb = dma_cb; ad->dma_status |= BM_STATUS_DMAING; + s->io_buffer_offset = 0; dma_cb(s, 0); } @@ -1023,7 +1049,7 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; - ahci_populate_sglist(ad, &s->sg); + ahci_populate_sglist(ad, &s->sg, 0); s->io_buffer_size = s->sg.size; DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); @@ -1037,7 +1063,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) uint8_t *p = s->io_buffer + s->io_buffer_index; int l = s->io_buffer_size - s->io_buffer_index; - if (ahci_populate_sglist(ad, &s->sg)) { + if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) { return 0; } @@ -1047,9 +1073,13 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) dma_buf_write(p, l, &s->sg); } + /* free sglist that was created in ahci_populate_sglist() */ + qemu_sglist_destroy(&s->sg); + /* update number of transferred bytes */ ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); s->io_buffer_index += l; + s->io_buffer_offset += l; DPRINTF(ad->port_no, "len=%#x\n", l); diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 7170bd9..bf7d313 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -393,6 +393,7 @@ struct IDEState { struct iovec iov; QEMUIOVector qiov; /* ATA DMA state */ + int io_buffer_offset; int io_buffer_size; QEMUSGList sg; /* PIO transfer handling */ diff --git a/hw/ivshmem.c b/hw/ivshmem.c index 0c58161..62fe53a 100644 --- a/hw/ivshmem.c +++ b/hw/ivshmem.c @@ -366,6 +366,10 @@ static void close_guest_eventfds(IVShmemState *s, int posn) { int i, guest_curr_max; + if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + return; + } + guest_curr_max = s->peers[posn].nb_eventfds; memory_region_transaction_begin(); @@ -381,17 +385,6 @@ static void close_guest_eventfds(IVShmemState *s, int posn) s->peers[posn].nb_eventfds = 0; } -static void setup_ioeventfds(IVShmemState *s) { - - int i, j; - - for (i = 0; i <= s->max_peer; i++) { - for (j = 0; j < s->peers[i].nb_eventfds; j++) { - ivshmem_add_eventfd(s, i, j); - } - } -} - /* this function increase the dynamic storage need to store data about other * guests */ static void increase_dynamic_storage(IVShmemState *s, int new_min_size) { @@ -677,7 +670,8 @@ static int pci_ivshmem_init(PCIDevice *dev) } if (s->role_val == IVSHMEM_PEER) { - error_set(&s->migration_blocker, QERR_DEVICE_FEATURE_BLOCKS_MIGRATION, "ivshmem", "peer mode"); + error_set(&s->migration_blocker, QERR_DEVICE_FEATURE_BLOCKS_MIGRATION, + "peer mode", "ivshmem"); migrate_add_blocker(s->migration_blocker); } @@ -691,10 +685,6 @@ static int pci_ivshmem_init(PCIDevice *dev) memory_region_init_io(&s->ivshmem_mmio, &ivshmem_mmio_ops, s, "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - setup_ioeventfds(s); - } - /* region for registers*/ pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ivshmem_mmio); diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c index c5d3711..53d13e3 100644 --- a/hw/kvm/i8254.c +++ b/hw/kvm/i8254.c @@ -35,7 +35,8 @@ typedef struct KVMPITState { PITCommonState pit; LostTickPolicy lost_tick_policy; - bool state_valid; + bool vm_stopped; + int64_t kernel_clock_offset; } KVMPITState; static int64_t abs64(int64_t v) @@ -43,19 +44,11 @@ static int64_t abs64(int64_t v) return v < 0 ? -v : v; } -static void kvm_pit_get(PITCommonState *pit) +static void kvm_pit_update_clock_offset(KVMPITState *s) { - KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); - struct kvm_pit_state2 kpit; - struct kvm_pit_channel_state *kchan; - struct PITChannelState *sc; int64_t offset, clock_offset; struct timespec ts; - int i, ret; - - if (s->state_valid) { - return; - } + int i; /* * Measure the delta between CLOCK_MONOTONIC, the base used for @@ -72,6 +65,21 @@ static void kvm_pit_get(PITCommonState *pit) clock_offset = offset; } } + s->kernel_clock_offset = clock_offset; +} + +static void kvm_pit_get(PITCommonState *pit) +{ + KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); + struct kvm_pit_state2 kpit; + struct kvm_pit_channel_state *kchan; + struct PITChannelState *sc; + int i, ret; + + /* No need to re-read the state if VM is stopped. */ + if (s->vm_stopped) { + return; + } if (kvm_has_pit_state2()) { ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); @@ -106,7 +114,7 @@ static void kvm_pit_get(PITCommonState *pit) sc->mode = kchan->mode; sc->bcd = kchan->bcd; sc->gate = kchan->gate; - sc->count_load_time = kchan->count_load_time + clock_offset; + sc->count_load_time = kchan->count_load_time + s->kernel_clock_offset; } sc = &pit->channels[0]; @@ -114,17 +122,23 @@ static void kvm_pit_get(PITCommonState *pit) pit_get_next_transition_time(sc, sc->count_load_time); } -static void kvm_pit_put(PITCommonState *s) +static void kvm_pit_put(PITCommonState *pit) { + KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); struct kvm_pit_state2 kpit; struct kvm_pit_channel_state *kchan; struct PITChannelState *sc; int i, ret; - kpit.flags = s->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; + /* The offset keeps changing as long as the VM is stopped. */ + if (s->vm_stopped) { + kvm_pit_update_clock_offset(s); + } + + kpit.flags = pit->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; for (i = 0; i < 3; i++) { kchan = &kpit.channels[i]; - sc = &s->channels[i]; + sc = &pit->channels[i]; kchan->count = sc->count; kchan->latched_count = sc->latched_count; kchan->count_latched = sc->count_latched; @@ -137,7 +151,7 @@ static void kvm_pit_put(PITCommonState *s) kchan->mode = sc->mode; kchan->bcd = sc->bcd; kchan->gate = sc->gate; - kchan->count_load_time = sc->count_load_time; + kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; } ret = kvm_vm_ioctl(kvm_state, @@ -211,10 +225,12 @@ static void kvm_pit_vm_state_change(void *opaque, int running, KVMPITState *s = opaque; if (running) { - s->state_valid = false; + kvm_pit_update_clock_offset(s); + s->vm_stopped = false; } else { + kvm_pit_update_clock_offset(s); kvm_pit_get(&s->pit); - s->state_valid = true; + s->vm_stopped = true; } } diff --git a/hw/megasas.c b/hw/megasas.c index c35a15d..c728aea 100644 --- a/hw/megasas.c +++ b/hw/megasas.c @@ -38,6 +38,7 @@ #define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ #define MEGASAS_MAX_ARRAYS 128 +#define MEGASAS_HBA_SERIAL "QEMU123456" #define NAA_LOCALLY_ASSIGNED_ID 0x3ULL #define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400 @@ -93,6 +94,7 @@ typedef struct MegasasState { int boot_event; uint64_t sas_addr; + char *hba_serial; uint64_t reply_queue_pa; void *reply_queue; @@ -698,8 +700,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) } memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20); - snprintf(info.serial_number, 32, "QEMU%08lx", - (unsigned long)s & 0xFFFFFFFF); + snprintf(info.serial_number, 32, "%s", s->hba_serial); snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION); memcpy(info.image_component[0].name, "APP", 3); memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9); @@ -2132,6 +2133,9 @@ static int megasas_scsi_init(PCIDevice *dev) s->sas_addr |= (PCI_SLOT(dev->devfn) << 8); s->sas_addr |= PCI_FUNC(dev->devfn); } + if (!s->hba_serial) { + s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL); + } if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) { s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE; } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) { @@ -2166,6 +2170,7 @@ static Property megasas_properties[] = { MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, MEGASAS_DEFAULT_FRAMES), + DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), DEFINE_PROP_HEX64("sas_address", MegasasState, sas_addr, 0), #ifdef USE_MSIX DEFINE_PROP_BIT("use_msix", MegasasState, flags, diff --git a/hw/mips_malta.c b/hw/mips_malta.c index 351c88e..ad23f26 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -48,6 +48,7 @@ #include "blockdev.h" #include "exec-memory.h" #include "sysbus.h" /* SysBusDevice */ +#include "vga-pci.h" //#define DEBUG_BOARD_INIT @@ -307,13 +307,9 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, return -EINVAL; } - if (asprintf(&name, "%s-msix", dev->name) == -1) { - return -ENOMEM; - } - + name = g_strdup_printf("%s-msix", dev->name); memory_region_init(&dev->msix_exclusive_bar, name, MSIX_EXCLUSIVE_BAR_SIZE); - - free(name); + g_free(name); ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr, MSIX_EXCLUSIVE_BAR_TABLE_OFFSET, &dev->msix_exclusive_bar, @@ -340,6 +336,15 @@ static void msix_free_irq_entries(PCIDevice *dev) } } +static void msix_clear_all_vectors(PCIDevice *dev) +{ + int vector; + + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { + msix_clr_pending(dev, vector); + } +} + /* Clean up resources for the device. */ void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) { @@ -394,7 +399,7 @@ void msix_load(PCIDevice *dev, QEMUFile *f) return; } - msix_free_irq_entries(dev); + msix_clear_all_vectors(dev); qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8); msix_update_function_masked(dev); @@ -440,7 +445,7 @@ void msix_reset(PCIDevice *dev) if (!msix_present(dev)) { return; } - msix_free_irq_entries(dev); + msix_clear_all_vectors(dev); dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &= ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE); diff --git a/hw/null-machine.c b/hw/null-machine.c new file mode 100644 index 0000000..69910d3 --- /dev/null +++ b/hw/null-machine.c @@ -0,0 +1,40 @@ +/* + * Empty machine + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * 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-common.h" +#include "hw/hw.h" +#include "hw/boards.h" + +static void machine_none_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ +} + +static QEMUMachine machine_none = { + .name = "none", + .desc = "empty machine", + .init = machine_none_init, + .max_cpus = 0, +}; + +static void register_machines(void) +{ + qemu_register_machine(&machine_none); +} + +machine_init(register_machines); + diff --git a/hw/openpic.c b/hw/openpic.c index 58ef871..b9d8568 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -130,6 +130,17 @@ enum { #define MPIC_CPU_REG_START 0x20000 #define MPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) +/* + * Block Revision Register1 (BRR1): QEMU does not fully emulate + * any version on MPIC. So to start with, set the IP version to 0. + * + * NOTE: This is Freescale MPIC specific register. Keep it here till + * this code is refactored for different variants of OPENPIC and MPIC. + */ +#define FSL_BRR1_IPID (0x0040 << 16) /* 16 bit IP-block ID */ +#define FSL_BRR1_IPMJ (0x00 << 8) /* 8 bit IP major number */ +#define FSL_BRR1_IPMN 0x00 /* 8 bit IP minor number */ + enum mpic_ide_bits { IDR_EP = 31, IDR_CI0 = 30, @@ -595,6 +606,8 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v if (addr & 0xF) return; switch (addr) { + case 0x00: /* Block Revision Register1 (BRR1) is Readonly */ + break; case 0x40: case 0x50: case 0x60: @@ -671,6 +684,7 @@ static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr) case 0x1090: /* PINT */ retval = 0x00000000; break; + case 0x00: /* Block Revision Register1 (BRR1) */ case 0x40: case 0x50: case 0x60: @@ -893,6 +907,9 @@ static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { + case 0x00: /* Block Revision Register1 (BRR1) */ + retval = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN; + break; case 0x80: /* PCTP */ retval = dst->pctp; break; @@ -51,6 +51,7 @@ #include "exec-memory.h" #include "arch_init.h" #include "bitmap.h" +#include "vga-pci.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS @@ -337,32 +338,37 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, /* various important CMOS locations needed by PC/Bochs bios */ /* memory size */ - val = 640; /* base memory in K */ + /* base memory (first MiB) */ + val = MIN(ram_size / 1024, 640); rtc_set_memory(s, 0x15, val); rtc_set_memory(s, 0x16, val >> 8); - - val = (ram_size / 1024) - 1024; + /* extended memory (next 64MiB) */ + if (ram_size > 1024 * 1024) { + val = (ram_size - 1024 * 1024) / 1024; + } else { + val = 0; + } if (val > 65535) val = 65535; rtc_set_memory(s, 0x17, val); rtc_set_memory(s, 0x18, val >> 8); rtc_set_memory(s, 0x30, val); rtc_set_memory(s, 0x31, val >> 8); - - if (above_4g_mem_size) { - rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16); - rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24); - rtc_set_memory(s, 0x5d, (uint64_t)above_4g_mem_size >> 32); - } - - if (ram_size > (16 * 1024 * 1024)) - val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536); - else + /* memory between 16MiB and 4GiB */ + if (ram_size > 16 * 1024 * 1024) { + val = (ram_size - 16 * 1024 * 1024) / 65536; + } else { val = 0; + } if (val > 65535) val = 65535; rtc_set_memory(s, 0x34, val); rtc_set_memory(s, 0x35, val >> 8); + /* memory above 4GiB */ + val = above_4g_mem_size / 65536; + rtc_set_memory(s, 0x5b, val); + rtc_set_memory(s, 0x5c, val >> 8); + rtc_set_memory(s, 0x5d, val >> 16); /* set the number of CPU */ rtc_set_memory(s, 0x5f, smp_cpus - 1); @@ -195,14 +195,10 @@ static inline DeviceState *isa_vga_init(ISABus *bus) return &dev->qdev; } -DeviceState *pci_vga_init(PCIBus *bus); int isa_vga_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift, MemoryRegion *address_space); -/* cirrus_vga.c */ -DeviceState *pci_cirrus_vga_init(PCIBus *bus); - /* ne2000.c */ static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd) { diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 0c0096f..88ff041 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -360,6 +360,14 @@ static QEMUMachine pc_machine_v1_2 = { #define PC_COMPAT_1_1 \ {\ + .driver = "virtio-scsi-pci",\ + .property = "hotplug",\ + .value = "off",\ + },{\ + .driver = "virtio-scsi-pci",\ + .property = "param_change",\ + .value = "off",\ + },{\ .driver = "VGA",\ .property = "vgamem_mb",\ .value = stringify(8),\ @@ -375,6 +383,10 @@ static QEMUMachine pc_machine_v1_2 = { .driver = "qxl",\ .property = "vgamem_mb",\ .value = stringify(8),\ + },{\ + .driver = "virtio-blk-pci",\ + .property = "config-wce",\ + .value = "off",\ } static QEMUMachine pc_machine_v1_1 = { diff --git a/hw/pci_host.c b/hw/pci_host.c index 8041778..3950e94 100644 --- a/hw/pci_host.c +++ b/hw/pci_host.c @@ -165,4 +165,16 @@ const MemoryRegionOps pci_host_data_be_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static const TypeInfo pci_host_type_info = { + .name = TYPE_PCI_HOST_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .abstract = true, + .instance_size = sizeof(PCIHostState), +}; + +static void pci_host_register_types(void) +{ + type_register_static(&pci_host_type_info); +} +type_init(pci_host_register_types) diff --git a/hw/pci_host.h b/hw/pci_host.h index 359e38f..4b9c300 100644 --- a/hw/pci_host.h +++ b/hw/pci_host.h @@ -30,8 +30,13 @@ #include "sysbus.h" +#define TYPE_PCI_HOST_BRIDGE "pci-host-bridge" +#define PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PCIHostState, (obj), TYPE_PCI_HOST_BRIDGE) + struct PCIHostState { SysBusDevice busdev; + MemoryRegion conf_mem; MemoryRegion data_mem; MemoryRegion mmcfg; diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c index 6a7d0c0..dced648 100644 --- a/hw/petalogix_ml605_mmu.c +++ b/hw/petalogix_ml605_mmu.c @@ -39,7 +39,8 @@ #include "microblaze_boot.h" #include "microblaze_pic_cpu.h" -#include "xilinx_axidma.h" + +#include "stream.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (32 * 1024 * 1024) @@ -76,7 +77,7 @@ petalogix_ml605_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { MemoryRegion *address_space_mem = get_system_memory(); - DeviceState *dev; + DeviceState *dev, *dma, *eth0; MicroBlazeCPU *cpu; CPUMBState *env; DriveInfo *dinfo; @@ -125,15 +126,18 @@ petalogix_ml605_init(ram_addr_t ram_size, /* 2 timers at irq 2 @ 100 Mhz. */ xilinx_timer_create(TIMER_BASEADDR, irq[2], 0, 100 * 1000000); - /* axi ethernet and dma initialization. TODO: Dynamically connect them. */ - { - static struct XilinxDMAConnection dmach; + /* axi ethernet and dma initialization. */ + dma = qdev_create(NULL, "xlnx.axi-dma"); - xilinx_axiethernet_create(&dmach, &nd_table[0], 0x82780000, - irq[3], 0x1000, 0x1000); - xilinx_axiethernetdma_create(&dmach, 0x84600000, - irq[1], irq[0], 100 * 1000000); - } + /* FIXME: attach to the sysbus instead */ + object_property_add_child(container_get(qdev_get_machine(), "/unattached"), + "xilinx-dma", OBJECT(dma), NULL); + + eth0 = xilinx_axiethernet_create(&nd_table[0], STREAM_SLAVE(dma), + 0x82780000, irq[3], 0x1000, 0x1000); + + xilinx_axiethernetdma_init(dma, STREAM_SLAVE(eth0), + 0x84600000, irq[1], irq[0], 100 * 1000000); microblaze_load_kernel(cpu, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE, machine_cpu_reset); diff --git a/hw/piix_pci.c b/hw/piix_pci.c index c497a01..537fc19 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -36,7 +36,9 @@ * http://download.intel.com/design/chipsets/datashts/29054901.pdf */ -typedef PCIHostState I440FXState; +typedef struct I440FXState { + PCIHostState parent_obj; +} I440FXState; #define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ #define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ @@ -225,7 +227,7 @@ static const VMStateDescription vmstate_i440fx = { static int i440fx_pcihost_initfn(SysBusDevice *dev) { - I440FXState *s = FROM_SYSBUS(I440FXState, dev); + PCIHostState *s = PCI_HOST_BRIDGE(dev); memory_region_init_io(&s->conf_mem, &pci_host_conf_le_ops, s, "pci-conf-idx", 4); @@ -267,14 +269,14 @@ static PCIBus *i440fx_common_init(const char *device_name, DeviceState *dev; PCIBus *b; PCIDevice *d; - I440FXState *s; + PCIHostState *s; PIIX3State *piix3; PCII440FXState *f; dev = qdev_create(NULL, "i440FX-pcihost"); - s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev)); + s = PCI_HOST_BRIDGE(dev); s->address_space = address_space_mem; - b = pci_bus_new(&s->busdev.qdev, NULL, pci_address_space, + b = pci_bus_new(dev, NULL, pci_address_space, address_space_io, 0); s->bus = b; object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); @@ -537,7 +539,7 @@ static void piix3_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_ISA; } -static TypeInfo piix3_info = { +static const TypeInfo piix3_info = { .name = "PIIX3", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PIIX3State), @@ -560,7 +562,7 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_ISA; }; -static TypeInfo piix3_xen_info = { +static const TypeInfo piix3_xen_info = { .name = "PIIX3-xen", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PIIX3State), @@ -584,7 +586,7 @@ static void i440fx_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_i440fx; } -static TypeInfo i440fx_info = { +static const TypeInfo i440fx_info = { .name = "i440FX", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCII440FXState), @@ -601,9 +603,9 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo i440fx_pcihost_info = { +static const TypeInfo i440fx_pcihost_info = { .name = "i440FX-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(I440FXState), .class_init = i440fx_pcihost_class_init, }; diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index aa4bbeb..951e407 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -15,7 +15,7 @@ obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o spapr_iommu.o obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-y += ppc440_bamboo.o # PowerPC E500 boards -obj-$(CONFIG_FDT) += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o +obj-$(CONFIG_FDT) += mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. obj-y += virtex_ml507.o # PowerPC OpenPIC @@ -26,3 +26,5 @@ obj-$(CONFIG_FDT) += ../device_tree.o obj-y += xilinx_ethlite.o obj-y := $(addprefix ../,$(obj-y)) + +obj-$(CONFIG_FDT) += e500.o mpc8544ds.o e500plat.o diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppc/e500.c index 8b9fd83..6f0de6d 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppc/e500.c @@ -1,5 +1,5 @@ /* - * QEMU PowerPC MPC8544DS board emulation + * QEMU PowerPC e500-based platforms * * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. * @@ -16,20 +16,21 @@ #include "config.h" #include "qemu-common.h" +#include "e500.h" #include "net.h" -#include "hw.h" -#include "pc.h" -#include "pci.h" -#include "boards.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci.h" +#include "hw/boards.h" #include "sysemu.h" #include "kvm.h" #include "kvm_ppc.h" #include "device_tree.h" -#include "openpic.h" -#include "ppc.h" -#include "loader.h" +#include "hw/openpic.h" +#include "hw/ppc.h" +#include "hw/loader.h" #include "elf.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec-memory.h" #include "host-utils.h" @@ -42,6 +43,7 @@ #define RAM_SIZES_ALIGN (64UL << 20) +/* TODO: parameterize */ #define MPC8544_CCSRBAR_BASE 0xE0000000ULL #define MPC8544_CCSRBAR_SIZE 0x00100000ULL #define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000ULL) @@ -66,18 +68,18 @@ static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic) int i; const uint32_t tmp[] = { /* IDSEL 0x11 J17 Slot 1 */ - 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, + 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, + 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, + 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, /* IDSEL 0x12 J16 Slot 2 */ - 0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, + 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, + 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, + 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, }; - for (i = 0; i < ARRAY_SIZE(tmp); i++) { + for (i = 0; i < (7 * 8); i++) { pci_map[i] = cpu_to_be32(tmp[i]); } } @@ -95,7 +97,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); - qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2, 0, 0); + qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2); qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); @@ -104,31 +106,28 @@ static void dt_serial_create(void *fdt, unsigned long long offset, } } -static int mpc8544_load_device_tree(CPUPPCState *env, +static int ppce500_load_device_tree(CPUPPCState *env, + PPCE500Params *params, target_phys_addr_t addr, - target_phys_addr_t ramsize, target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) + target_phys_addr_t initrd_size) { int ret = -1; - uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) }; + uint64_t mem_reg_property[] = { 0, cpu_to_be64(params->ram_size) }; int fdt_size; void *fdt; uint8_t hypercall[16]; uint32_t clock_freq = 400000000; uint32_t tb_freq = 400000000; int i; - const char *compatible = "MPC8544DS\0MPC85xxDS"; - int compatible_len = sizeof("MPC8544DS\0MPC85xxDS"); + const char *toplevel_compat = NULL; /* user override */ char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; - char model[] = "MPC8544DS"; char soc[128]; char mpic[128]; uint32_t mpic_ph; char gutil[128]; char pci[128]; - uint32_t pci_map[9 * 8]; + uint32_t pci_map[7 * 8]; uint32_t pci_ranges[14] = { 0x2000000, 0x0, 0xc0000000, @@ -145,14 +144,9 @@ static int mpc8544_load_device_tree(CPUPPCState *env, machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); if (machine_opts) { - const char *tmp; dumpdtb = qemu_opt_get(machine_opts, "dumpdtb"); dtb_file = qemu_opt_get(machine_opts, "dtb"); - tmp = qemu_opt_get(machine_opts, "dt_compatible"); - if (tmp) { - compatible = tmp; - compatible_len = strlen(compatible) + 1; - } + toplevel_compat = qemu_opt_get(machine_opts, "dt_compatible"); } if (dtb_file) { @@ -175,8 +169,6 @@ static int mpc8544_load_device_tree(CPUPPCState *env, } /* Manipulate device tree in memory. */ - qemu_devtree_setprop_string(fdt, "/", "model", model); - qemu_devtree_setprop(fdt, "/", "compatible", compatible, compatible_len); qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); @@ -201,7 +193,7 @@ static int mpc8544_load_device_tree(CPUPPCState *env, } ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); + params->kernel_cmdline); if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -282,18 +274,15 @@ static int mpc8544_load_device_tree(CPUPPCState *env, MPC8544_MPIC_REGS_BASE - MPC8544_CCSRBAR_BASE); qemu_devtree_add_subnode(fdt, mpic); qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + qemu_devtree_setprop_string(fdt, mpic, "compatible", "chrp,open-pic"); qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_BASE - MPC8544_CCSRBAR_BASE, 0x40000); qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); - qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 4); + qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2); mpic_ph = qemu_devtree_alloc_phandle(fdt); qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); - qemu_devtree_setprop(fdt, mpic, "big-endian", NULL, 0); - qemu_devtree_setprop(fdt, mpic, "single-cpu-affinity", NULL, 0); - qemu_devtree_setprop_cell(fdt, mpic, "last-interrupt-source", 255); /* * We have to generate ser1 first, because Linux takes the first @@ -323,7 +312,7 @@ static int mpc8544_load_device_tree(CPUPPCState *env, pci_map_create(fdt, pci_map, qemu_devtree_get_phandle(fdt, mpic)); qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, sizeof(pci_map)); qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); - qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2, 0, 0); + qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2); qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255); for (i = 0; i < 14; i++) { pci_ranges[i] = cpu_to_be32(pci_ranges[i]); @@ -337,6 +326,13 @@ static int mpc8544_load_device_tree(CPUPPCState *env, qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); + params->fixup_devtree(params, fdt); + + if (toplevel_compat) { + qemu_devtree_setprop(fdt, "/", "compatible", toplevel_compat, + strlen(toplevel_compat) + 1); + } + done: if (dumpdtb) { /* Dump the dtb to a file and quit */ @@ -388,7 +384,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env) env->tlb_dirty = true; } -static void mpc8544ds_cpu_reset_sec(void *opaque) +static void ppce500_cpu_reset_sec(void *opaque) { PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; @@ -401,7 +397,7 @@ static void mpc8544ds_cpu_reset_sec(void *opaque) env->exception_index = EXCP_HLT; } -static void mpc8544ds_cpu_reset(void *opaque) +static void ppce500_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; @@ -417,12 +413,7 @@ static void mpc8544ds_cpu_reset(void *opaque) mmubooke_create_initial_mapping(env); } -static void mpc8544ds_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *cpu_model) +void ppce500_init(PPCE500Params *params) { MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -443,8 +434,8 @@ static void mpc8544ds_init(ram_addr_t ram_size, CPUPPCState *firstenv = NULL; /* Setup CPUs */ - if (cpu_model == NULL) { - cpu_model = "e500v2_v30"; + if (params->cpu_model == NULL) { + params->cpu_model = "e500v2_v30"; } irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); @@ -453,7 +444,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, PowerPCCPU *cpu; qemu_irq *input; - cpu = cpu_ppc_init(cpu_model); + cpu = cpu_ppc_init(params->cpu_model); if (cpu == NULL) { fprintf(stderr, "Unable to initialize CPU!\n"); exit(1); @@ -478,11 +469,11 @@ static void mpc8544ds_init(ram_addr_t ram_size, /* Primary CPU */ struct boot_info *boot_info; boot_info = g_malloc0(sizeof(struct boot_info)); - qemu_register_reset(mpc8544ds_cpu_reset, cpu); + qemu_register_reset(ppce500_cpu_reset, cpu); env->load_info = boot_info; } else { /* Secondary CPUs */ - qemu_register_reset(mpc8544ds_cpu_reset_sec, cpu); + qemu_register_reset(ppce500_cpu_reset_sec, cpu); } } @@ -542,43 +533,45 @@ static void mpc8544ds_init(ram_addr_t ram_size, sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); /* Load kernel. */ - if (kernel_filename) { - kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); + if (params->kernel_filename) { + kernel_size = load_uimage(params->kernel_filename, &entry, + &loadaddr, NULL); if (kernel_size < 0) { - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); + kernel_size = load_elf(params->kernel_filename, NULL, NULL, + &elf_entry, &elf_lowaddr, NULL, 1, + ELF_MACHINE, 0); entry = elf_entry; loadaddr = elf_lowaddr; } /* XXX try again as binary */ if (kernel_size < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); + params->kernel_filename); exit(1); } } /* Load initrd. */ - if (initrd_filename) { + if (params->initrd_filename) { initrd_base = (kernel_size + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; - initrd_size = load_image_targphys(initrd_filename, initrd_base, + initrd_size = load_image_targphys(params->initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); + params->initrd_filename); exit(1); } } /* If we're loading a kernel directly, we must load the device tree too. */ - if (kernel_filename) { + if (params->kernel_filename) { struct boot_info *boot_info; int dt_size; dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base, - initrd_size, kernel_cmdline); + dt_size = ppce500_load_device_tree(env, params, dt_base, initrd_base, + initrd_size); if (dt_size < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); @@ -594,17 +587,3 @@ static void mpc8544ds_init(ram_addr_t ram_size, kvmppc_init(); } } - -static QEMUMachine mpc8544ds_machine = { - .name = "mpc8544ds", - .desc = "mpc8544ds", - .init = mpc8544ds_init, - .max_cpus = 15, -}; - -static void mpc8544ds_machine_init(void) -{ - qemu_register_machine(&mpc8544ds_machine); -} - -machine_init(mpc8544ds_machine_init); diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h new file mode 100644 index 0000000..7ae87f4 --- /dev/null +++ b/hw/ppc/e500.h @@ -0,0 +1,21 @@ +#ifndef PPCE500_H +#define PPCE500_H + +typedef struct PPCE500Params { + /* Standard QEMU machine init params */ + ram_addr_t ram_size; + const char *boot_device; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; + const char *cpu_model; + + /* e500-specific params */ + + /* required -- must at least add toplevel board compatible */ + void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); +} PPCE500Params; + +void ppce500_init(PPCE500Params *params); + +#endif diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c new file mode 100644 index 0000000..60a5cb3 --- /dev/null +++ b/hw/ppc/e500plat.c @@ -0,0 +1,60 @@ +/* + * Generic device-tree-driven paravirt PPC e500 platform + * + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This 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. + */ + +#include "config.h" +#include "qemu-common.h" +#include "e500.h" +#include "../boards.h" +#include "device_tree.h" + +static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) +{ + const char model[] = "QEMU ppce500"; + const char compatible[] = "fsl,qemu-e500"; + + qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_devtree_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); +} + +static void e500plat_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + PPCE500Params params = { + .ram_size = ram_size, + .boot_device = boot_device, + .kernel_filename = kernel_filename, + .kernel_cmdline = kernel_cmdline, + .initrd_filename = initrd_filename, + .cpu_model = cpu_model, + .fixup_devtree = e500plat_fixup_devtree, + }; + + ppce500_init(¶ms); +} + +static QEMUMachine e500plat_machine = { + .name = "ppce500", + .desc = "generic paravirt e500 platform", + .init = e500plat_init, + .max_cpus = 15, +}; + +static void e500plat_machine_init(void) +{ + qemu_register_machine(&e500plat_machine); +} + +machine_init(e500plat_machine_init); diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c new file mode 100644 index 0000000..984d21c --- /dev/null +++ b/hw/ppc/mpc8544ds.c @@ -0,0 +1,61 @@ +/* + * Support for the PPC e500-based mpc8544ds board + * + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This 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. + */ + +#include "config.h" +#include "qemu-common.h" +#include "e500.h" +#include "../boards.h" +#include "device_tree.h" + +static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) +{ + const char model[] = "MPC8544DS"; + const char compatible[] = "MPC8544DS\0MPC85xxDS"; + + qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_devtree_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); +} + +static void mpc8544ds_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + PPCE500Params params = { + .ram_size = ram_size, + .boot_device = boot_device, + .kernel_filename = kernel_filename, + .kernel_cmdline = kernel_cmdline, + .initrd_filename = initrd_filename, + .cpu_model = cpu_model, + .fixup_devtree = mpc8544ds_fixup_devtree, + }; + + ppce500_init(¶ms); +} + + +static QEMUMachine ppce500_machine = { + .name = "mpc8544ds", + .desc = "mpc8544ds", + .init = mpc8544ds_init, + .max_cpus = 15, +}; + +static void ppce500_machine_init(void) +{ + qemu_register_machine(&ppce500_machine); +} + +machine_init(ppce500_machine_init); diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index 0dd4dab..c198071 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -216,7 +216,8 @@ static void bamboo_init(ram_addr_t ram_size, ram_bases, ram_sizes, 1); /* PCI */ - dev = sysbus_create_varargs("ppc4xx-pcihost", PPC440EP_PCI_CONFIG, + 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]], NULL); diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h index b511020..5cd78b6 100644 --- a/hw/ppc4xx.h +++ b/hw/ppc4xx.h @@ -53,6 +53,8 @@ void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, target_phys_addr_t *ram_sizes, int do_init); +#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost" + PCIBus *ppc4xx_pci_init(CPUPPCState *env, qemu_irq pci_irqs[4], target_phys_addr_t config_space, target_phys_addr_t int_ack, diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c index 203c3cd..a14fd42 100644 --- a/hw/ppc4xx_pci.c +++ b/hw/ppc4xx_pci.c @@ -45,11 +45,14 @@ struct PCITargetMap { uint32_t la; }; +#define PPC4xx_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PPC4xxPCIState, (obj), TYPE_PPC4xx_PCI_HOST_BRIDGE) + #define PPC4xx_PCI_NR_PMMS 3 #define PPC4xx_PCI_NR_PTMS 2 struct PPC4xxPCIState { - PCIHostState pci_state; + PCIHostState parent_obj; struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; @@ -93,16 +96,18 @@ static uint64_t pci4xx_cfgaddr_read(void *opaque, target_phys_addr_t addr, unsigned size) { PPC4xxPCIState *ppc4xx_pci = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); - return ppc4xx_pci->pci_state.config_reg; + return phb->config_reg; } static void pci4xx_cfgaddr_write(void *opaque, target_phys_addr_t addr, uint64_t value, unsigned size) { PPC4xxPCIState *ppc4xx_pci = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); - ppc4xx_pci->pci_state.config_reg = value & ~0x3; + phb->config_reg = value & ~0x3; } static const MemoryRegionOps pci4xx_cfgaddr_ops = { @@ -335,17 +340,17 @@ static int ppc4xx_pcihost_initfn(SysBusDevice *dev) PCIBus *b; int i; - h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); - s = DO_UPCAST(PPC4xxPCIState, pci_state, h); + h = PCI_HOST_BRIDGE(dev); + s = PPC4xx_PCI_HOST_BRIDGE(dev); for (i = 0; i < ARRAY_SIZE(s->irq); i++) { sysbus_init_irq(dev, &s->irq[i]); } - b = pci_register_bus(&s->pci_state.busdev.qdev, NULL, ppc4xx_pci_set_irq, + b = pci_register_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq, ppc4xx_pci_map_irq, s->irq, get_system_memory(), get_system_io(), 0, 4); - s->pci_state.bus = b; + h->bus = b; pci_create_simple(b, 0, "ppc4xx-host-bridge"); @@ -377,7 +382,7 @@ static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_OTHER; } -static TypeInfo ppc4xx_host_bridge_info = { +static const TypeInfo ppc4xx_host_bridge_info = { .name = "ppc4xx-host-bridge", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -393,9 +398,9 @@ static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_ppc4xx_pci; } -static TypeInfo ppc4xx_pcihost_info = { - .name = "ppc4xx-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo ppc4xx_pcihost_info = { + .name = TYPE_PPC4xx_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(PPC4xxPCIState), .class_init = ppc4xx_pcihost_class_init, }; diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h index af75e45..7d08418 100644 --- a/hw/ppc_mac.h +++ b/hw/ppc_mac.h @@ -55,6 +55,7 @@ qemu_irq *heathrow_pic_init(MemoryRegion **pmem, int nb_cpus, qemu_irq **irqs); /* Grackle PCI */ +#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io); diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index 4e2a6e6..e95cfe8 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -52,7 +52,6 @@ #include "adb.h" #include "mac_dbdma.h" #include "nvram.h" -#include "pc.h" #include "pci.h" #include "net.h" #include "sysemu.h" @@ -68,6 +67,7 @@ #include "hw/usb.h" #include "blockdev.h" #include "exec-memory.h" +#include "vga-pci.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index f2c6908..1dcd8a6 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -29,7 +29,6 @@ #include "adb.h" #include "mac_dbdma.h" #include "nvram.h" -#include "pc.h" #include "sysemu.h" #include "net.h" #include "isa.h" @@ -44,6 +43,7 @@ #include "kvm_ppc.h" #include "blockdev.h" #include "exec-memory.h" +#include "vga-pci.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index be2b268..592b7b2 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -39,6 +39,7 @@ #include "blockdev.h" #include "arch_init.h" #include "exec-memory.h" +#include "vga-pci.h" //#define HARD_DEBUG_PPC_IO //#define DEBUG_PPC_IO @@ -470,7 +471,6 @@ static void ppc_prep_init (ram_addr_t ram_size, uint32_t kernel_base, initrd_base; long kernel_size, initrd_size; DeviceState *dev; - SysBusDevice *sys; PCIHostState *pcihost; PCIBus *pci_bus; PCIDevice *pci; @@ -583,8 +583,7 @@ static void ppc_prep_init (ram_addr_t ram_size, } dev = qdev_create(NULL, "raven-pcihost"); - sys = sysbus_from_qdev(dev); - pcihost = DO_UPCAST(PCIHostState, busdev, sys); + pcihost = PCI_HOST_BRIDGE(dev); pcihost->address_space = get_system_memory(); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); qdev_init_nofail(dev); diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index 0f60b24..92b1dc0 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -72,8 +72,14 @@ struct pci_inbound { uint32_t piwar; }; +#define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost" + +#define PPC_E500_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PPCE500PCIState, (obj), TYPE_PPC_E500_PCI_HOST_BRIDGE) + struct PPCE500PCIState { - PCIHostState pci_state; + PCIHostState parent_obj; + struct pci_outbound pob[PPCE500_PCI_NR_POBS]; struct pci_inbound pib[PPCE500_PCI_NR_PIBS]; uint32_t gasket_time; @@ -310,17 +316,17 @@ static int e500_pcihost_initfn(SysBusDevice *dev) MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *address_space_io = get_system_io(); - h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); - s = DO_UPCAST(PPCE500PCIState, pci_state, h); + h = PCI_HOST_BRIDGE(dev); + s = PPC_E500_PCI_HOST_BRIDGE(dev); for (i = 0; i < ARRAY_SIZE(s->irq); i++) { sysbus_init_irq(dev, &s->irq[i]); } - b = pci_register_bus(&s->pci_state.busdev.qdev, NULL, mpc85xx_pci_set_irq, + b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq, mpc85xx_pci_map_irq, s->irq, address_space_mem, address_space_io, PCI_DEVFN(0x11, 0), 4); - s->pci_state.bus = b; + h->bus = b; pci_create_simple(b, 0, "e500-host-bridge"); @@ -350,7 +356,7 @@ static void e500_host_bridge_class_init(ObjectClass *klass, void *data) dc->desc = "Host bridge"; } -static TypeInfo e500_host_bridge_info = { +static const TypeInfo e500_host_bridge_info = { .name = "e500-host-bridge", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -366,9 +372,9 @@ static void e500_pcihost_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_ppce500_pci; } -static TypeInfo e500_pcihost_info = { - .name = "e500-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo e500_pcihost_info = { + .name = TYPE_PPC_E500_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(PPCE500PCIState), .class_init = e500_pcihost_class_init, }; diff --git a/hw/prep_pci.c b/hw/prep_pci.c index 38dbff4..cc44e61 100644 --- a/hw/prep_pci.c +++ b/hw/prep_pci.c @@ -28,8 +28,14 @@ #include "pc.h" #include "exec-memory.h" +#define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost" + +#define RAVEN_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE) + typedef struct PRePPCIState { - PCIHostState host_state; + PCIHostState parent_obj; + MemoryRegion intack; qemu_irq irq[4]; } PREPPCIState; @@ -42,9 +48,10 @@ static inline uint32_t PPC_PCIIO_config(target_phys_addr_t addr) { int i; - for(i = 0; i < 11; i++) { - if ((addr & (1 << (11 + i))) != 0) + for (i = 0; i < 11; i++) { + if ((addr & (1 << (11 + i))) != 0) { break; + } } return (addr & 0x7ff) | (i << 11); } @@ -53,14 +60,16 @@ static void ppc_pci_io_write(void *opaque, target_phys_addr_t addr, uint64_t val, unsigned int size) { PREPPCIState *s = opaque; - pci_data_write(s->host_state.bus, PPC_PCIIO_config(addr), val, size); + PCIHostState *phb = PCI_HOST_BRIDGE(s); + pci_data_write(phb->bus, PPC_PCIIO_config(addr), val, size); } static uint64_t ppc_pci_io_read(void *opaque, target_phys_addr_t addr, unsigned int size) { PREPPCIState *s = opaque; - return pci_data_read(s->host_state.bus, PPC_PCIIO_config(addr), size); + PCIHostState *phb = PCI_HOST_BRIDGE(s); + return pci_data_read(phb->bus, PPC_PCIIO_config(addr), size); } static const MemoryRegionOps PPC_PCIIO_ops = { @@ -96,8 +105,8 @@ static void prep_set_irq(void *opaque, int irq_num, int level) static int raven_pcihost_init(SysBusDevice *dev) { - PCIHostState *h = FROM_SYSBUS(PCIHostState, dev); - PREPPCIState *s = DO_UPCAST(PREPPCIState, host_state, h); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev); MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *address_space_io = get_system_io(); PCIBus *bus; @@ -107,7 +116,7 @@ static int raven_pcihost_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq[i]); } - bus = pci_register_bus(&h->busdev.qdev, NULL, + bus = pci_register_bus(DEVICE(dev), NULL, prep_set_irq, prep_map_irq, s->irq, address_space_mem, address_space_io, 0, 4); h->bus = bus; @@ -166,7 +175,7 @@ static void raven_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo raven_info = { +static const TypeInfo raven_info = { .name = "raven", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RavenPCIState), @@ -183,9 +192,9 @@ static void raven_pcihost_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo raven_pcihost_info = { - .name = "raven-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo raven_pcihost_info = { + .name = TYPE_RAVEN_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(PREPPCIState), .class_init = raven_pcihost_class_init, }; diff --git a/hw/puv3.c b/hw/puv3.c new file mode 100644 index 0000000..43f7216 --- /dev/null +++ b/hw/puv3.c @@ -0,0 +1,131 @@ +/* + * Generic PKUnity SoC machine and board descriptor + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "console.h" +#include "elf.h" +#include "exec-memory.h" +#include "sysbus.h" +#include "boards.h" +#include "loader.h" +#include "pc.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +#define KERNEL_LOAD_ADDR 0x03000000 +#define KERNEL_MAX_SIZE 0x00800000 /* Just a guess */ + +static void puv3_intc_cpu_handler(void *opaque, int irq, int level) +{ + CPUUniCore32State *env = opaque; + + assert(irq == 0); + if (level) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void puv3_soc_init(CPUUniCore32State *env) +{ + qemu_irq *cpu_intc, irqs[PUV3_IRQS_NR]; + DeviceState *dev; + MemoryRegion *i8042 = g_new(MemoryRegion, 1); + int i; + + /* Initialize interrupt controller */ + cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler, env, 1); + dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, *cpu_intc); + for (i = 0; i < PUV3_IRQS_NR; i++) { + irqs[i] = qdev_get_gpio_in(dev, i); + } + + /* Initialize minimal necessary devices for kernel booting */ + sysbus_create_simple("puv3_pm", PUV3_PM_BASE, NULL); + sysbus_create_simple("puv3_dma", PUV3_DMA_BASE, NULL); + sysbus_create_simple("puv3_ost", PUV3_OST_BASE, irqs[PUV3_IRQS_OST0]); + sysbus_create_varargs("puv3_gpio", PUV3_GPIO_BASE, + irqs[PUV3_IRQS_GPIOLOW0], irqs[PUV3_IRQS_GPIOLOW1], + irqs[PUV3_IRQS_GPIOLOW2], irqs[PUV3_IRQS_GPIOLOW3], + irqs[PUV3_IRQS_GPIOLOW4], irqs[PUV3_IRQS_GPIOLOW5], + irqs[PUV3_IRQS_GPIOLOW6], irqs[PUV3_IRQS_GPIOLOW7], + irqs[PUV3_IRQS_GPIOHIGH], NULL); + + /* Keyboard (i8042), mouse disabled for nographic */ + i8042_mm_init(irqs[PUV3_IRQS_PS2_KBD], NULL, i8042, PUV3_REGS_OFFSET, 4); + memory_region_add_subregion(get_system_memory(), PUV3_PS2_BASE, i8042); +} + +static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size) +{ + MemoryRegion *ram_memory = g_new(MemoryRegion, 1); + + /* SDRAM at address zero. */ + memory_region_init_ram(ram_memory, "puv3.ram", ram_size); + vmstate_register_ram_global(ram_memory); + memory_region_add_subregion(get_system_memory(), 0, ram_memory); +} + +static void puv3_load_kernel(const char *kernel_filename) +{ + int size; + + assert(kernel_filename != NULL); + + /* only zImage format supported */ + size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, + KERNEL_MAX_SIZE); + if (size < 0) { + hw_error("Load kernel error: '%s'\n", kernel_filename); + } + + /* cheat curses that we have a graphic console, only under ocd console */ + graphic_console_init(NULL, NULL, NULL, NULL, NULL); +} + +static void puv3_init(ram_addr_t ram_size, const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUUniCore32State *env; + + if (initrd_filename) { + hw_error("Please use kernel built-in initramdisk.\n"); + } + + if (!cpu_model) { + cpu_model = "UniCore-II"; + } + + env = cpu_init(cpu_model); + if (!env) { + hw_error("Unable to find CPU definition\n"); + } + + puv3_soc_init(env); + puv3_board_init(env, ram_size); + puv3_load_kernel(kernel_filename); +} + +static QEMUMachine puv3_machine = { + .name = "puv3", + .desc = "PKUnity Version-3 based on UniCore32", + .init = puv3_init, + .is_default = 1, + .use_scsi = 0, +}; + +static void puv3_machine_init(void) +{ + qemu_register_machine(&puv3_machine); +} + +machine_init(puv3_machine_init) diff --git a/hw/puv3.h b/hw/puv3.h new file mode 100644 index 0000000..f37adcb --- /dev/null +++ b/hw/puv3.h @@ -0,0 +1,49 @@ +/* + * Misc PKUnity SoC declarations + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_HW_PUV3_H +#define QEMU_HW_PUV3_H + +#define PUV3_REGS_OFFSET (0x1000) /* 4K is reasonable */ + +/* PKUnity System bus (AHB): 0xc0000000 - 0xedffffff (640MB) */ +#define PUV3_DMA_BASE (0xc0200000) /* AHB-4 */ + +/* PKUnity Peripheral bus (APB): 0xee000000 - 0xefffffff (128MB) */ +#define PUV3_GPIO_BASE (0xee500000) /* APB-5 */ +#define PUV3_INTC_BASE (0xee600000) /* APB-6 */ +#define PUV3_OST_BASE (0xee800000) /* APB-8 */ +#define PUV3_PM_BASE (0xeea00000) /* APB-10 */ +#define PUV3_PS2_BASE (0xeeb00000) /* APB-11 */ + +/* Hardware interrupts */ +#define PUV3_IRQS_NR (32) + +#define PUV3_IRQS_GPIOLOW0 (0) +#define PUV3_IRQS_GPIOLOW1 (1) +#define PUV3_IRQS_GPIOLOW2 (2) +#define PUV3_IRQS_GPIOLOW3 (3) +#define PUV3_IRQS_GPIOLOW4 (4) +#define PUV3_IRQS_GPIOLOW5 (5) +#define PUV3_IRQS_GPIOLOW6 (6) +#define PUV3_IRQS_GPIOLOW7 (7) +#define PUV3_IRQS_GPIOHIGH (8) +#define PUV3_IRQS_PS2_KBD (22) +#define PUV3_IRQS_PS2_AUX (23) +#define PUV3_IRQS_OST0 (26) + +/* All puv3_*.c use DPRINTF for debug. */ +#ifdef DEBUG_PUV3 +#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#endif /* !QEMU_HW_PUV3_H */ diff --git a/hw/puv3_dma.c b/hw/puv3_dma.c new file mode 100644 index 0000000..85b97bf --- /dev/null +++ b/hw/puv3_dma.c @@ -0,0 +1,109 @@ +/* + * DMA device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "hw.h" +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +#define PUV3_DMA_CH_NR (6) +#define PUV3_DMA_CH_MASK (0xff) +#define PUV3_DMA_CH(offset) ((offset) >> 8) + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + uint32_t reg_CFG[PUV3_DMA_CH_NR]; +} PUV3DMAState; + +static uint64_t puv3_dma_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3DMAState *s = opaque; + uint32_t ret = 0; + + assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); + + switch (offset & PUV3_DMA_CH_MASK) { + case 0x10: + ret = s->reg_CFG[PUV3_DMA_CH(offset)]; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + + return ret; +} + +static void puv3_dma_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3DMAState *s = opaque; + + assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); + + switch (offset & PUV3_DMA_CH_MASK) { + case 0x10: + s->reg_CFG[PUV3_DMA_CH(offset)] = value; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); +} + +static const MemoryRegionOps puv3_dma_ops = { + .read = puv3_dma_read, + .write = puv3_dma_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_dma_init(SysBusDevice *dev) +{ + PUV3DMAState *s = FROM_SYSBUS(PUV3DMAState, dev); + int i; + + for (i = 0; i < PUV3_DMA_CH_NR; i++) { + s->reg_CFG[i] = 0x0; + } + + memory_region_init_io(&s->iomem, &puv3_dma_ops, s, "puv3_dma", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_dma_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_dma_init; +} + +static const TypeInfo puv3_dma_info = { + .name = "puv3_dma", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3DMAState), + .class_init = puv3_dma_class_init, +}; + +static void puv3_dma_register_type(void) +{ + type_register_static(&puv3_dma_info); +} + +type_init(puv3_dma_register_type) diff --git a/hw/puv3_gpio.c b/hw/puv3_gpio.c new file mode 100644 index 0000000..9436e6c --- /dev/null +++ b/hw/puv3_gpio.c @@ -0,0 +1,141 @@ +/* + * GPIO device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "hw.h" +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq[9]; + + uint32_t reg_GPLR; + uint32_t reg_GPDR; + uint32_t reg_GPIR; +} PUV3GPIOState; + +static uint64_t puv3_gpio_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3GPIOState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x00: + ret = s->reg_GPLR; + break; + case 0x04: + ret = s->reg_GPDR; + break; + case 0x20: + ret = s->reg_GPIR; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + + return ret; +} + +static void puv3_gpio_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3GPIOState *s = opaque; + + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); + switch (offset) { + case 0x04: + s->reg_GPDR = value; + break; + case 0x08: + if (s->reg_GPDR & value) { + s->reg_GPLR |= value; + } else { + DPRINTF("Write gpio input port error!"); + } + break; + case 0x0c: + if (s->reg_GPDR & value) { + s->reg_GPLR &= ~value; + } else { + DPRINTF("Write gpio input port error!"); + } + break; + case 0x10: /* GRER */ + case 0x14: /* GFER */ + case 0x18: /* GEDR */ + break; + case 0x20: /* GPIR */ + s->reg_GPIR = value; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } +} + +static const MemoryRegionOps puv3_gpio_ops = { + .read = puv3_gpio_read, + .write = puv3_gpio_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_gpio_init(SysBusDevice *dev) +{ + PUV3GPIOState *s = FROM_SYSBUS(PUV3GPIOState, dev); + + s->reg_GPLR = 0; + s->reg_GPDR = 0; + + /* FIXME: these irqs not handled yet */ + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW0]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW1]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW2]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW3]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW4]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW5]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW6]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW7]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOHIGH]); + + memory_region_init_io(&s->iomem, &puv3_gpio_ops, s, "puv3_gpio", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_gpio_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_gpio_init; +} + +static const TypeInfo puv3_gpio_info = { + .name = "puv3_gpio", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3GPIOState), + .class_init = puv3_gpio_class_init, +}; + +static void puv3_gpio_register_type(void) +{ + type_register_static(&puv3_gpio_info); +} + +type_init(puv3_gpio_register_type) diff --git a/hw/puv3_intc.c b/hw/puv3_intc.c new file mode 100644 index 0000000..9e0b975 --- /dev/null +++ b/hw/puv3_intc.c @@ -0,0 +1,135 @@ +/* + * INTC device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq parent_irq; + + uint32_t reg_ICMR; + uint32_t reg_ICPR; +} PUV3INTCState; + +/* Update interrupt status after enabled or pending bits have been changed. */ +static void puv3_intc_update(PUV3INTCState *s) +{ + if (s->reg_ICMR & s->reg_ICPR) { + qemu_irq_raise(s->parent_irq); + } else { + qemu_irq_lower(s->parent_irq); + } +} + +/* Process a change in an external INTC input. */ +static void puv3_intc_handler(void *opaque, int irq, int level) +{ + PUV3INTCState *s = opaque; + + DPRINTF("irq 0x%x, level 0x%x\n", irq, level); + if (level) { + s->reg_ICPR |= (1 << irq); + } else { + s->reg_ICPR &= ~(1 << irq); + } + puv3_intc_update(s); +} + +static uint64_t puv3_intc_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3INTCState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x04: /* INTC_ICMR */ + ret = s->reg_ICMR; + break; + case 0x0c: /* INTC_ICIP */ + ret = s->reg_ICPR; /* the same value with ICPR */ + break; + default: + DPRINTF("Bad offset %x\n", (int)offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + return ret; +} + +static void puv3_intc_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3INTCState *s = opaque; + + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); + switch (offset) { + case 0x00: /* INTC_ICLR */ + case 0x14: /* INTC_ICCR */ + break; + case 0x04: /* INTC_ICMR */ + s->reg_ICMR = value; + break; + default: + DPRINTF("Bad offset 0x%x\n", (int)offset); + return; + } + puv3_intc_update(s); +} + +static const MemoryRegionOps puv3_intc_ops = { + .read = puv3_intc_read, + .write = puv3_intc_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_intc_init(SysBusDevice *dev) +{ + PUV3INTCState *s = FROM_SYSBUS(PUV3INTCState, dev); + + qdev_init_gpio_in(&s->busdev.qdev, puv3_intc_handler, PUV3_IRQS_NR); + sysbus_init_irq(&s->busdev, &s->parent_irq); + + s->reg_ICMR = 0; + s->reg_ICPR = 0; + + memory_region_init_io(&s->iomem, &puv3_intc_ops, s, "puv3_intc", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_intc_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_intc_init; +} + +static const TypeInfo puv3_intc_info = { + .name = "puv3_intc", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3INTCState), + .class_init = puv3_intc_class_init, +}; + +static void puv3_intc_register_type(void) +{ + type_register_static(&puv3_intc_info); +} + +type_init(puv3_intc_register_type) diff --git a/hw/puv3_ost.c b/hw/puv3_ost.c new file mode 100644 index 0000000..dd30cad --- /dev/null +++ b/hw/puv3_ost.c @@ -0,0 +1,151 @@ +/* + * OSTimer device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "sysbus.h" +#include "ptimer.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +/* puv3 ostimer implementation. */ +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + QEMUBH *bh; + qemu_irq irq; + ptimer_state *ptimer; + + uint32_t reg_OSMR0; + uint32_t reg_OSCR; + uint32_t reg_OSSR; + uint32_t reg_OIER; +} PUV3OSTState; + +static uint64_t puv3_ost_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3OSTState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x10: /* Counter Register */ + ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer); + break; + case 0x14: /* Status Register */ + ret = s->reg_OSSR; + break; + case 0x1c: /* Interrupt Enable Register */ + ret = s->reg_OIER; + break; + default: + DPRINTF("Bad offset %x\n", (int)offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + return ret; +} + +static void puv3_ost_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3OSTState *s = opaque; + + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); + switch (offset) { + case 0x00: /* Match Register 0 */ + s->reg_OSMR0 = value; + if (s->reg_OSMR0 > s->reg_OSCR) { + ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR); + } else { + ptimer_set_count(s->ptimer, s->reg_OSMR0 + + (0xffffffff - s->reg_OSCR)); + } + ptimer_run(s->ptimer, 2); + break; + case 0x14: /* Status Register */ + assert(value == 0); + if (s->reg_OSSR) { + s->reg_OSSR = value; + qemu_irq_lower(s->irq); + } + break; + case 0x1c: /* Interrupt Enable Register */ + s->reg_OIER = value; + break; + default: + DPRINTF("Bad offset %x\n", (int)offset); + } +} + +static const MemoryRegionOps puv3_ost_ops = { + .read = puv3_ost_read, + .write = puv3_ost_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void puv3_ost_tick(void *opaque) +{ + PUV3OSTState *s = opaque; + + DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n", + s->reg_OSCR, s->reg_OSMR0); + + s->reg_OSCR = s->reg_OSMR0; + if (s->reg_OIER) { + s->reg_OSSR = 1; + qemu_irq_raise(s->irq); + } +} + +static int puv3_ost_init(SysBusDevice *dev) +{ + PUV3OSTState *s = FROM_SYSBUS(PUV3OSTState, dev); + + s->reg_OIER = 0; + s->reg_OSSR = 0; + s->reg_OSMR0 = 0; + s->reg_OSCR = 0; + + sysbus_init_irq(dev, &s->irq); + + s->bh = qemu_bh_new(puv3_ost_tick, s); + s->ptimer = ptimer_init(s->bh); + ptimer_set_freq(s->ptimer, 50 * 1000 * 1000); + + memory_region_init_io(&s->iomem, &puv3_ost_ops, s, "puv3_ost", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_ost_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_ost_init; +} + +static const TypeInfo puv3_ost_info = { + .name = "puv3_ost", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3OSTState), + .class_init = puv3_ost_class_init, +}; + +static void puv3_ost_register_type(void) +{ + type_register_static(&puv3_ost_info); +} + +type_init(puv3_ost_register_type) diff --git a/hw/puv3_pm.c b/hw/puv3_pm.c new file mode 100644 index 0000000..621c968 --- /dev/null +++ b/hw/puv3_pm.c @@ -0,0 +1,149 @@ +/* + * Power Management device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "hw.h" +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t reg_PMCR; + uint32_t reg_PCGR; + uint32_t reg_PLL_SYS_CFG; + uint32_t reg_PLL_DDR_CFG; + uint32_t reg_PLL_VGA_CFG; + uint32_t reg_DIVCFG; +} PUV3PMState; + +static uint64_t puv3_pm_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3PMState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x14: + ret = s->reg_PCGR; + break; + case 0x18: + ret = s->reg_PLL_SYS_CFG; + break; + case 0x1c: + ret = s->reg_PLL_DDR_CFG; + break; + case 0x20: + ret = s->reg_PLL_VGA_CFG; + break; + case 0x24: + ret = s->reg_DIVCFG; + break; + case 0x28: /* PLL SYS STATUS */ + ret = 0x00002401; + break; + case 0x2c: /* PLL DDR STATUS */ + ret = 0x00100c00; + break; + case 0x30: /* PLL VGA STATUS */ + ret = 0x00003801; + break; + case 0x34: /* DIV STATUS */ + ret = 0x22f52015; + break; + case 0x38: /* SW RESET */ + ret = 0x0; + break; + case 0x44: /* PLL DFC DONE */ + ret = 0x7; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + + return ret; +} + +static void puv3_pm_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3PMState *s = opaque; + + switch (offset) { + case 0x0: + s->reg_PMCR = value; + break; + case 0x14: + s->reg_PCGR = value; + break; + case 0x18: + s->reg_PLL_SYS_CFG = value; + break; + case 0x1c: + s->reg_PLL_DDR_CFG = value; + break; + case 0x20: + s->reg_PLL_VGA_CFG = value; + break; + case 0x24: + case 0x38: + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); +} + +static const MemoryRegionOps puv3_pm_ops = { + .read = puv3_pm_read, + .write = puv3_pm_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_pm_init(SysBusDevice *dev) +{ + PUV3PMState *s = FROM_SYSBUS(PUV3PMState, dev); + + s->reg_PCGR = 0x0; + + memory_region_init_io(&s->iomem, &puv3_pm_ops, s, "puv3_pm", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_pm_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_pm_init; +} + +static const TypeInfo puv3_pm_info = { + .name = "puv3_pm", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3PMState), + .class_init = puv3_pm_class_init, +}; + +static void puv3_pm_register_type(void) +{ + type_register_static(&puv3_pm_info); +} + +type_init(puv3_pm_register_type) diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c index b22a37a..33b7f79 100644 --- a/hw/qdev-monitor.c +++ b/hw/qdev-monitor.c @@ -443,7 +443,7 @@ DeviceState *qdev_device_add(QemuOpts *opts) bus = qbus_find_recursive(sysbus_get_default(), NULL, k->bus_type); if (!bus) { qerror_report(QERR_NO_BUS_FOR_DEVICE, - driver, k->bus_type); + k->bus_type, driver); return NULL; } } @@ -543,7 +543,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent) qdev_print_props(mon, dev, DEVICE_CLASS(class)->props, indent); class = object_class_get_parent(class); } while (class != object_class_by_name(TYPE_DEVICE)); - bus_print_dev(dev->parent_bus, mon, dev, indent + 2); + bus_print_dev(dev->parent_bus, mon, dev, indent); QLIST_FOREACH(child, &dev->child_bus, sibling) { qbus_print(mon, child, indent); } @@ -159,7 +159,6 @@ int qdev_init(DeviceState *dev) rc = dc->init(dev); if (rc < 0) { - object_unparent(OBJECT(dev)); qdev_free(dev); return rc; } @@ -243,7 +242,6 @@ void qbus_reset_all_fn(void *opaque) int qdev_simple_unplug_cb(DeviceState *dev) { /* just zap it */ - object_unparent(OBJECT(dev)); qdev_free(dev); return 0; } diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index b8a857d..4981a02 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -761,6 +761,7 @@ static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf) switch (length) { case 0: case 3: /* USB-specific. */ + default: xfer = 0; break; case 1: @@ -784,6 +785,7 @@ static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf) switch (length) { case 0: case 3: /* USB-specific. */ + default: xfer = 0; break; case 1: diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index c8d5edd..1585683 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -175,6 +175,8 @@ static void scsi_aio_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; bdrv_acct_done(s->qdev.conf.bs, &r->acct); if (ret < 0) { @@ -238,10 +240,9 @@ static void scsi_dma_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - if (r->req.aiocb != NULL) { - r->req.aiocb = NULL; - bdrv_acct_done(s->qdev.conf.bs, &r->acct); - } + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + bdrv_acct_done(s->qdev.conf.bs, &r->acct); if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -270,10 +271,9 @@ static void scsi_read_complete(void * opaque, int ret) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); int n; - if (r->req.aiocb != NULL) { - r->req.aiocb = NULL; - bdrv_acct_done(s->qdev.conf.bs, &r->acct); - } + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + bdrv_acct_done(s->qdev.conf.bs, &r->acct); if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -637,7 +637,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { buflen = 8; outbuf[4] = 0; - outbuf[5] = 0x60; /* write_same 10/16 supported */ + outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; outbuf[7] = 0; break; @@ -1449,6 +1449,89 @@ invalid_field: return; } +typedef struct UnmapCBData { + SCSIDiskReq *r; + uint8_t *inbuf; + int count; +} UnmapCBData; + +static void scsi_unmap_complete(void *opaque, int ret) +{ + UnmapCBData *data = opaque; + SCSIDiskReq *r = data->r; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + uint64_t sector_num; + uint32_t nb_sectors; + + r->req.aiocb = NULL; + if (ret < 0) { + if (scsi_handle_rw_error(r, -ret)) { + goto done; + } + } + + if (data->count > 0 && !r->req.io_canceled) { + sector_num = ldq_be_p(&data->inbuf[0]); + nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL; + if (sector_num > sector_num + nb_sectors || + sector_num + nb_sectors - 1 > s->qdev.max_lba) { + scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); + goto done; + } + + r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs, + sector_num * (s->qdev.blocksize / 512), + nb_sectors * (s->qdev.blocksize / 512), + scsi_unmap_complete, data); + data->count--; + data->inbuf += 16; + return; + } + +done: + if (data->count == 0) { + scsi_req_complete(&r->req, GOOD); + } + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } + g_free(data); +} + +static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) +{ + uint8_t *p = inbuf; + int len = r->req.cmd.xfer; + UnmapCBData *data; + + if (len < 8) { + goto invalid_param_len; + } + if (len < lduw_be_p(&p[0]) + 2) { + goto invalid_param_len; + } + if (len < lduw_be_p(&p[2]) + 8) { + goto invalid_param_len; + } + if (lduw_be_p(&p[2]) & 15) { + goto invalid_param_len; + } + + data = g_new0(UnmapCBData, 1); + data->r = r; + data->inbuf = &p[8]; + data->count = lduw_be_p(&p[2]) >> 4; + + /* The matching unref is in scsi_unmap_complete, before data is freed. */ + scsi_req_ref(&r->req); + scsi_unmap_complete(data, 0); + return; + +invalid_param_len: + scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); + return; +} + static void scsi_disk_emulate_write_data(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); @@ -1468,6 +1551,10 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req) scsi_disk_emulate_mode_select(r, r->iov.iov_base); break; + case UNMAP: + scsi_disk_emulate_unmap(r, r->iov.iov_base); + break; + default: abort(); } @@ -1702,6 +1789,9 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case MODE_SELECT_10: DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer); break; + case UNMAP: + DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer); + break; case WRITE_SAME_10: nb_sectors = lduw_be_p(&req->cmd.buf[7]); goto write_same; @@ -1712,7 +1802,8 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); return 0; } - if (r->req.cmd.lba > s->qdev.max_lba) { + if (r->req.cmd.lba > r->req.cmd.lba + nb_sectors || + r->req.cmd.lba + nb_sectors - 1 > s->qdev.max_lba) { goto illegal_lba; } @@ -2067,6 +2158,7 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { [SEEK_10] = &scsi_disk_emulate_reqops, [MODE_SELECT] = &scsi_disk_emulate_reqops, [MODE_SELECT_10] = &scsi_disk_emulate_reqops, + [UNMAP] = &scsi_disk_emulate_reqops, [WRITE_SAME_10] = &scsi_disk_emulate_reqops, [WRITE_SAME_16] = &scsi_disk_emulate_reqops, @@ -2329,6 +2421,7 @@ static TypeInfo scsi_cd_info = { #ifdef __linux__ static Property scsi_block_properties[] = { DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.bs), + DEFINE_PROP_INT32("bootindex", SCSIDiskState, qdev.conf.bootindex, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 8d51060..a5eb663 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -479,7 +479,8 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, } static Property scsi_generic_properties[] = { - DEFINE_BLOCK_PROPERTIES(SCSIDevice, conf), + DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.bs), + DEFINE_PROP_INT32("bootindex", SCSIDevice, conf.bootindex, -1), DEFINE_PROP_END_OF_LIST(), }; @@ -33,6 +33,7 @@ #include "hw.h" #include "block.h" #include "sd.h" +#include "bitmap.h" //#define DEBUG_SD 1 @@ -82,8 +83,8 @@ struct SDState { uint32_t card_status; uint8_t sd_status[64]; uint32_t vhs; - int wp_switch; - int *wp_groups; + bool wp_switch; + unsigned long *wp_groups; uint64_t size; int blk_len; uint32_t erase_start; @@ -92,12 +93,12 @@ struct SDState { int pwd_len; int function_group[6]; - int spi, mmc; + bool spi, mmc; int current_cmd; /* True if we will handle the next command as an ACMD. Note that this does * *not* track the APP_CMD status bit! */ - int expecting_acmd; + bool expecting_acmd; int blk_written; uint64_t data_start; uint32_t data_offset; @@ -107,7 +108,7 @@ struct SDState { BlockDriverState *bdrv; uint8_t *buf; - int enable; + bool enable; int buswidth, highspeed; }; @@ -431,6 +432,11 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response) response[3] = (sd->vhs >> 0) & 0xff; } +static inline uint64_t sd_addr_to_wpnum(uint64_t addr) +{ + return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); +} + void sd_reset(SDState *sd) { uint64_t size; @@ -443,7 +449,7 @@ void sd_reset(SDState *sd) } size = sect << 9; - sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; + sect = sd_addr_to_wpnum(size) + 1; sd->state = sd_idle_state; sd->rca = 0x0000; @@ -456,15 +462,15 @@ void sd_reset(SDState *sd) if (sd->wp_groups) g_free(sd->wp_groups); - sd->wp_switch = sd->bdrv ? bdrv_is_read_only(sd->bdrv) : 0; - sd->wp_groups = (int *) g_malloc0(sizeof(int) * sect); + sd->wp_switch = sd->bdrv ? bdrv_is_read_only(sd->bdrv) : false; + sd->wp_groups = bitmap_new(sect); memset(sd->function_group, 0, sizeof(int) * 6); sd->erase_start = 0; sd->erase_end = 0; sd->size = size; sd->blk_len = 0x200; sd->pwd_len = 0; - sd->expecting_acmd = 0; + sd->expecting_acmd = false; } static void sd_cardchange(void *opaque, bool load) @@ -486,7 +492,7 @@ static const BlockDevOps sd_block_ops = { whether card should be in SSI or MMC/SD mode. It is also up to the board to ensure that ssi transfers only occur when the chip select is asserted. */ -SDState *sd_init(BlockDriverState *bs, int is_spi, int is_mmc) +SDState *sd_init(BlockDriverState *bs, bool is_spi, bool is_mmc) { SDState *sd; @@ -494,7 +500,7 @@ SDState *sd_init(BlockDriverState *bs, int is_spi, int is_mmc) sd->buf = qemu_blockalign(bs, 512); sd->spi = is_spi; sd->mmc = is_mmc; - sd->enable = 1; + sd->enable = true; sd->bdrv = bs; sd_reset(sd); if (sd->bdrv) { @@ -520,17 +526,17 @@ static void sd_erase(SDState *sd) return; } - start = sd->erase_start >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); - end = sd->erase_end >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); + start = sd_addr_to_wpnum(sd->erase_start); + end = sd_addr_to_wpnum(sd->erase_end); sd->erase_start = 0; sd->erase_end = 0; sd->csd[14] |= 0x40; - for (i = start; i <= end; i ++) - if (sd->wp_groups[i]) + for (i = start; i <= end; i++) { + if (test_bit(i, sd->wp_groups)) { sd->card_status |= WP_ERASE_SKIP; + } + } } static uint32_t sd_wpbits(SDState *sd, uint64_t addr) @@ -538,11 +544,13 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr) uint32_t i, wpnum; uint32_t ret = 0; - wpnum = addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); + wpnum = sd_addr_to_wpnum(addr); - for (i = 0; i < 32; i ++, wpnum ++, addr += WPGROUP_SIZE) - if (addr < sd->size && sd->wp_groups[wpnum]) + for (i = 0; i < 32; i++, wpnum++, addr += WPGROUP_SIZE) { + if (addr < sd->size && test_bit(wpnum, sd->wp_groups)) { ret |= (1 << i); + } + } return ret; } @@ -618,10 +626,9 @@ static void mmc_function_switch(SDState *sd, uint32_t arg) } } -static inline int sd_wp_addr(SDState *sd, uint32_t addr) +static inline bool sd_wp_addr(SDState *sd, uint64_t addr) { - return sd->wp_groups[addr >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)]; + return test_bit(sd_addr_to_wpnum(addr), sd->wp_groups); } static void sd_lock_command(SDState *sd) @@ -644,8 +651,7 @@ static void sd_lock_command(SDState *sd) sd->card_status |= LOCK_UNLOCK_FAILED; return; } - memset(sd->wp_groups, 0, sizeof(int) * (sd->size >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT))); + bitmap_zero(sd->wp_groups, sd_addr_to_wpnum(sd->size) + 1); sd->csd[14] &= ~0x10; sd->card_status &= ~CARD_IS_LOCKED; sd->pwd_len = 0; @@ -1151,8 +1157,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } sd->state = sd_programming_state; - sd->wp_groups[addr >> (HWBLOCK_SHIFT + - SECTOR_SHIFT + WPGROUP_SHIFT)] = 1; + set_bit(sd_addr_to_wpnum(addr), sd->wp_groups); /* Bzzzzzzztt .... Operation complete. */ sd->state = sd_transfer_state; return sd_r1b; @@ -1171,8 +1176,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } sd->state = sd_programming_state; - sd->wp_groups[addr >> (HWBLOCK_SHIFT + - SECTOR_SHIFT + WPGROUP_SHIFT)] = 0; + clear_bit(sd_addr_to_wpnum(addr), sd->wp_groups); /* Bzzzzzzztt .... Operation complete. */ sd->state = sd_transfer_state; return sd_r1b; @@ -1290,7 +1294,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, if (sd->rca != rca) return sd_r0; - sd->expecting_acmd = 1; + sd->expecting_acmd = true; sd->card_status |= APP_CMD; return sd_r1; @@ -1472,7 +1476,7 @@ int sd_do_command(SDState *sd, SDRequest *req, if (sd->card_status & CARD_IS_LOCKED) { if (!cmd_valid_while_locked(sd, req)) { sd->card_status |= ILLEGAL_COMMAND; - sd->expecting_acmd = 0; + sd->expecting_acmd = false; fprintf(stderr, "SD: Card is locked\n"); rtype = sd_illegal; goto send_response; @@ -1483,7 +1487,7 @@ int sd_do_command(SDState *sd, SDRequest *req, sd_set_mode(sd); if (sd->expecting_acmd) { - sd->expecting_acmd = 0; + sd->expecting_acmd = false; rtype = sd_app_command(sd, *req); } else { rtype = sd_normal_command(sd, *req); @@ -1881,17 +1885,17 @@ uint8_t sd_read_data(SDState *sd) return ret; } -int sd_data_ready(SDState *sd) +bool sd_data_ready(SDState *sd) { return sd->state == sd_sendingdata_state; } -void sd_enable(SDState *sd, int enable) +void sd_enable(SDState *sd, bool enable) { sd->enable = enable; } -int sd_is_mmc(SDState *sd) +bool sd_is_mmc(SDState *sd) { return sd->mmc; } @@ -67,15 +67,15 @@ typedef struct { typedef struct SDState SDState; -SDState *sd_init(BlockDriverState *bs, int is_spi, int is_mmc); +SDState *sd_init(BlockDriverState *bs, bool is_spi, bool is_mmc); void sd_reset(SDState *sd); int sd_do_command(SDState *sd, SDRequest *req, uint8_t *response); void sd_write_data(SDState *sd, uint8_t value); uint8_t sd_read_data(SDState *sd); void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert); -int sd_data_ready(SDState *sd); -void sd_enable(SDState *sd, int enable); -int sd_is_mmc(SDState *sd); +bool sd_data_ready(SDState *sd); +void sd_enable(SDState *sd, bool enable); +bool sd_is_mmc(SDState *sd); #endif /* __hw_sd_h */ @@ -253,7 +253,6 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) ++devfn) { PCIDevice *affected_dev = shpc->sec_bus->devices[devfn]; if (affected_dev) { - object_unparent(OBJECT(affected_dev)); qdev_free(&affected_dev->qdev); } } @@ -41,12 +41,15 @@ #include "hw/spapr_vio.h" #include "hw/spapr_pci.h" #include "hw/xics.h" +#include "hw/msi.h" #include "kvm.h" #include "kvm_ppc.h" #include "pci.h" +#include "vga-pci.h" #include "exec-memory.h" +#include "hw/usb.h" #include <libfdt.h> @@ -78,16 +81,15 @@ #define SPAPR_PCI_MEM_WIN_ADDR (0x10000000000ULL + 0xA0000000) #define SPAPR_PCI_MEM_WIN_SIZE 0x20000000 #define SPAPR_PCI_IO_WIN_ADDR (0x10000000000ULL + 0x80000000) +#define SPAPR_PCI_MSI_WIN_ADDR (0x10000000000ULL + 0x90000000) #define PHANDLE_XICP 0x00001111 sPAPREnvironment *spapr; -qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, - enum xics_irq_type type) +int spapr_allocate_irq(int hint, enum xics_irq_type type) { - uint32_t irq; - qemu_irq qirq; + int irq; if (hint) { irq = hint; @@ -96,16 +98,40 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, irq = spapr->next_irq++; } - qirq = xics_assign_irq(spapr->icp, irq, type); - if (!qirq) { - return NULL; + /* Configure irq type */ + if (!xics_get_qirq(spapr->icp, irq)) { + return 0; } - if (irq_num) { - *irq_num = irq; + xics_set_irq_type(spapr->icp, irq, type); + + return irq; +} + +/* Allocate block of consequtive IRQs, returns a number of the first */ +int spapr_allocate_irq_block(int num, enum xics_irq_type type) +{ + int first = -1; + int i; + + for (i = 0; i < num; ++i) { + int irq; + + irq = spapr_allocate_irq(0, type); + if (!irq) { + return -1; + } + + if (0 == i) { + first = irq; + } + + /* If the above doesn't create a consecutive block then that's + * an internal bug */ + assert(irq == (first + i)); } - return qirq; + return first; } static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr) @@ -257,6 +283,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop)))); } _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); + _FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width))); + _FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height))); + _FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth))); _FDT((fdt_end_node(fdt))); @@ -481,7 +510,7 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, } QLIST_FOREACH(phb, &spapr->phbs, list) { - ret = spapr_populate_pci_devices(phb, PHANDLE_XICP, fdt); + ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); } if (ret < 0) { @@ -503,7 +532,9 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, } } - spapr_populate_chosen_stdout(fdt, spapr->vio_bus); + if (!spapr->has_graphics) { + spapr_populate_chosen_stdout(fdt, spapr->vio_bus); + } _FDT((fdt_pack(fdt))); @@ -532,8 +563,6 @@ static void spapr_reset(void *opaque) { sPAPREnvironment *spapr = (sPAPREnvironment *)opaque; - fprintf(stderr, "sPAPR reset\n"); - /* flush out the hash table */ memset(spapr->htab, 0, spapr->htab_size); @@ -556,6 +585,23 @@ static void spapr_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); } +/* Returns whether we want to use VGA or not */ +static int spapr_vga_init(PCIBus *pci_bus) +{ + switch (vga_interface_type) { + case VGA_STD: + pci_vga_init(pci_bus); + return 1; + case VGA_NONE: + return 0; + default: + fprintf(stderr, "This vga model is not supported," + "currently it only supports -vga std\n"); + exit(0); + break; + } +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(ram_addr_t ram_size, const char *boot_device, @@ -566,6 +612,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, { PowerPCCPU *cpu; CPUPPCState *env; + PCIHostState *phb; int i; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -576,6 +623,8 @@ static void ppc_spapr_init(ram_addr_t ram_size, long pteg_shift = 17; char *filename; + msi_supported = true; + spapr = g_malloc0(sizeof(*spapr)); QLIST_INIT(&spapr->phbs); @@ -687,10 +736,14 @@ static void ppc_spapr_init(ram_addr_t ram_size, } /* Set up PCI */ + spapr_pci_rtas_init(); + spapr_create_phb(spapr, "pci", SPAPR_PCI_BUID, SPAPR_PCI_MEM_WIN_ADDR, SPAPR_PCI_MEM_WIN_SIZE, - SPAPR_PCI_IO_WIN_ADDR); + SPAPR_PCI_IO_WIN_ADDR, + SPAPR_PCI_MSI_WIN_ADDR); + phb = PCI_HOST_BRIDGE(QLIST_FIRST(&spapr->phbs)); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -710,20 +763,25 @@ static void ppc_spapr_init(ram_addr_t ram_size, spapr_vscsi_create(spapr->vio_bus); } + /* Graphics */ + if (spapr_vga_init(phb->bus)) { + spapr->has_graphics = true; + } + + if (usb_enabled) { + pci_create_simple(phb->bus, -1, "pci-ohci"); + if (spapr->has_graphics) { + usbdevice_create("keyboard"); + usbdevice_create("mouse"); + } + } + if (rma_size < (MIN_RMA_SLOF << 20)) { fprintf(stderr, "qemu: pSeries SLOF firmware requires >= " "%ldM guest RMA (Real Mode Area memory)\n", MIN_RMA_SLOF); exit(1); } - fprintf(stderr, "sPAPR memory map:\n"); - fprintf(stderr, "RTAS : 0x%08lx..%08lx\n", - (unsigned long)spapr->rtas_addr, - (unsigned long)(spapr->rtas_addr + spapr->rtas_size - 1)); - fprintf(stderr, "FDT : 0x%08lx..%08lx\n", - (unsigned long)spapr->fdt_addr, - (unsigned long)(spapr->fdt_addr + FDT_MAX_SIZE - 1)); - if (kernel_filename) { uint64_t lowaddr = 0; @@ -739,8 +797,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, kernel_filename); exit(1); } - fprintf(stderr, "Kernel : 0x%08x..%08lx\n", - KERNEL_LOAD_ADDR, KERNEL_LOAD_ADDR + kernel_size - 1); /* load initrd */ if (initrd_filename) { @@ -755,8 +811,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, initrd_filename); exit(1); } - fprintf(stderr, "Ramdisk : 0x%08lx..%08lx\n", - (long)initrd_base, (long)(initrd_base + initrd_size - 1)); } else { initrd_base = 0; initrd_size = 0; @@ -770,10 +824,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, exit(1); } g_free(filename); - fprintf(stderr, "Firmware load : 0x%08x..%08lx\n", - 0, fw_size); - fprintf(stderr, "Firmware runtime : 0x%08lx..%08lx\n", - load_limit, (unsigned long)spapr->fdt_addr); spapr->entry_point = 0x100; @@ -23,6 +23,7 @@ typedef struct sPAPREnvironment { int next_irq; int rtc_offset; char *cpu_model; + bool has_graphics; } sPAPREnvironment; #define H_SUCCESS 0 @@ -288,17 +289,17 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode, target_ulong *args); -qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, - enum xics_irq_type type); +int spapr_allocate_irq(int hint, enum xics_irq_type type); +int spapr_allocate_irq_block(int num, enum xics_irq_type type); -static inline qemu_irq spapr_allocate_msi(uint32_t hint, uint32_t *irq_num) +static inline int spapr_allocate_msi(int hint) { - return spapr_allocate_irq(hint, irq_num, XICS_MSI); + return spapr_allocate_irq(hint, XICS_MSI); } -static inline qemu_irq spapr_allocate_lsi(uint32_t hint, uint32_t *irq_num) +static inline int spapr_allocate_lsi(int hint) { - return spapr_allocate_irq(hint, irq_num, XICS_LSI); + return spapr_allocate_irq(hint, XICS_LSI); } static inline uint32_t rtas_ld(target_ulong phys, int n) @@ -336,6 +337,8 @@ void spapr_iommu_init(void); DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size); void spapr_tce_free(DMAContext *dma); int spapr_dma_dt(void *fdt, int node_off, const char *propname, - DMAContext *dma); + uint32_t liobn, uint64_t window, uint32_t size); +int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, + DMAContext *dma); #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/hw/spapr_iommu.c b/hw/spapr_iommu.c index 388ffa4..53b7317 100644 --- a/hw/spapr_iommu.c +++ b/hw/spapr_iommu.c @@ -216,31 +216,47 @@ void spapr_iommu_init(void) } int spapr_dma_dt(void *fdt, int node_off, const char *propname, - DMAContext *dma) + uint32_t liobn, uint64_t window, uint32_t size) { - if (dma) { - sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); - uint32_t dma_prop[] = {cpu_to_be32(tcet->liobn), - 0, 0, - 0, cpu_to_be32(tcet->window_size)}; - int ret; - - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); - if (ret < 0) { - return ret; - } + uint32_t dma_prop[5]; + int ret; + + dma_prop[0] = cpu_to_be32(liobn); + dma_prop[1] = cpu_to_be32(window >> 32); + dma_prop[2] = cpu_to_be32(window & 0xFFFFFFFF); + dma_prop[3] = 0; /* window size is 32 bits */ + dma_prop[4] = cpu_to_be32(size); + + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); + if (ret < 0) { + return ret; + } - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); - if (ret < 0) { - return ret; - } + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); + if (ret < 0) { + return ret; + } - ret = fdt_setprop(fdt, node_off, propname, dma_prop, - sizeof(dma_prop)); - if (ret < 0) { - return ret; - } + ret = fdt_setprop(fdt, node_off, propname, dma_prop, sizeof(dma_prop)); + if (ret < 0) { + return ret; } return 0; } + +int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, + DMAContext *iommu) +{ + if (!iommu) { + return 0; + } + + if (iommu->translate == spapr_tce_translate) { + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, iommu); + return spapr_dma_dt(fdt, node_off, propname, + tcet->liobn, 0, tcet->window_size); + } + + return -1; +} diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index 01e54f3..bd3f131 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -169,7 +169,7 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, } if (sdev->signal_state & 1) { - qemu_irq_pulse(sdev->qirq); + qemu_irq_pulse(spapr_vio_qirq(sdev)); } return size; diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index b2e4f78..661c05b 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -24,32 +24,59 @@ */ #include "hw.h" #include "pci.h" +#include "msi.h" +#include "msix.h" #include "pci_host.h" #include "hw/spapr.h" #include "hw/spapr_pci.h" #include "exec-memory.h" #include <libfdt.h> +#include "trace.h" #include "hw/pci_internals.h" -static PCIDevice *find_dev(sPAPREnvironment *spapr, - uint64_t buid, uint32_t config_addr) -{ - int devfn = (config_addr >> 8) & 0xFF; - sPAPRPHBState *phb; +/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ +#define RTAS_QUERY_FN 0 +#define RTAS_CHANGE_FN 1 +#define RTAS_RESET_FN 2 +#define RTAS_CHANGE_MSI_FN 3 +#define RTAS_CHANGE_MSIX_FN 4 - QLIST_FOREACH(phb, &spapr->phbs, list) { - BusChild *kid; +/* Interrupt types to return on RTAS_CHANGE_* */ +#define RTAS_TYPE_MSI 1 +#define RTAS_TYPE_MSIX 2 + +static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) +{ + sPAPRPHBState *sphb; - if (phb->buid != buid) { + QLIST_FOREACH(sphb, &spapr->phbs, list) { + if (sphb->buid != buid) { continue; } + return sphb; + } + + return NULL; +} + +static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid, + uint32_t config_addr) +{ + sPAPRPHBState *sphb = find_phb(spapr, buid); + PCIHostState *phb = PCI_HOST_BRIDGE(sphb); + BusState *bus = BUS(phb->bus); + BusChild *kid; + int devfn = (config_addr >> 8) & 0xFF; + + if (!phb) { + return NULL; + } - QTAILQ_FOREACH(kid, &phb->host_state.bus->qbus.children, sibling) { - PCIDevice *dev = (PCIDevice *)kid->child; - if (dev->devfn == devfn) { - return dev; - } + QTAILQ_FOREACH(kid, &bus->children, sibling) { + PCIDevice *dev = (PCIDevice *)kid->child; + if (dev->devfn == devfn) { + return dev; } } @@ -199,6 +226,191 @@ static void rtas_write_pci_config(sPAPREnvironment *spapr, finish_write_pci_config(spapr, 0, addr, size, val, rets); } +/* + * Find an entry with config_addr or returns the empty one if not found AND + * alloc_new is set. + * At the moment the msi_table entries are never released so there is + * no point to look till the end of the list if we need to find the free entry. + */ +static int spapr_msicfg_find(sPAPRPHBState *phb, uint32_t config_addr, + bool alloc_new) +{ + int i; + + for (i = 0; i < SPAPR_MSIX_MAX_DEVS; ++i) { + if (!phb->msi_table[i].nvec) { + break; + } + if (phb->msi_table[i].config_addr == config_addr) { + return i; + } + } + if ((i < SPAPR_MSIX_MAX_DEVS) && alloc_new) { + trace_spapr_pci_msi("Allocating new MSI config", i, config_addr); + return i; + } + + return -1; +} + +/* + * Set MSI/MSIX message data. + * This is required for msi_notify()/msix_notify() which + * will write at the addresses via spapr_msi_write(). + */ +static void spapr_msi_setmsg(PCIDevice *pdev, target_phys_addr_t addr, + bool msix, unsigned req_num) +{ + unsigned i; + MSIMessage msg = { .address = addr, .data = 0 }; + + if (!msix) { + msi_set_message(pdev, msg); + trace_spapr_pci_msi_setup(pdev->name, 0, msg.address); + return; + } + + for (i = 0; i < req_num; ++i) { + msg.address = addr | (i << 2); + msix_set_message(pdev, i, msg); + trace_spapr_pci_msi_setup(pdev->name, i, msg.address); + } +} + +static void rtas_ibm_change_msi(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint32_t config_addr = rtas_ld(args, 0); + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + unsigned int func = rtas_ld(args, 3); + unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */ + unsigned int seq_num = rtas_ld(args, 5); + unsigned int ret_intr_type; + int ndev, irq; + sPAPRPHBState *phb = NULL; + PCIDevice *pdev = NULL; + + switch (func) { + case RTAS_CHANGE_MSI_FN: + case RTAS_CHANGE_FN: + ret_intr_type = RTAS_TYPE_MSI; + break; + case RTAS_CHANGE_MSIX_FN: + ret_intr_type = RTAS_TYPE_MSIX; + break; + default: + fprintf(stderr, "rtas_ibm_change_msi(%u) is not implemented\n", func); + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Fins sPAPRPHBState */ + phb = find_phb(spapr, buid); + if (phb) { + pdev = find_dev(spapr, buid, config_addr); + } + if (!phb || !pdev) { + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Releasing MSIs */ + if (!req_num) { + ndev = spapr_msicfg_find(phb, config_addr, false); + if (ndev < 0) { + trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + trace_spapr_pci_msi("Released MSIs", ndev, config_addr); + rtas_st(rets, 0, 0); + rtas_st(rets, 1, 0); + return; + } + + /* Enabling MSI */ + + /* Find a device number in the map to add or reuse the existing one */ + ndev = spapr_msicfg_find(phb, config_addr, true); + if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) { + fprintf(stderr, "No free entry for a new MSI device\n"); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + trace_spapr_pci_msi("Configuring MSI", ndev, config_addr); + + /* Check if there is an old config and MSI number has not changed */ + if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) { + /* Unexpected behaviour */ + fprintf(stderr, "Cannot reuse MSI config for device#%d", ndev); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + + /* There is no cached config, allocate MSIs */ + if (!phb->msi_table[ndev].nvec) { + irq = spapr_allocate_irq_block(req_num, XICS_MSI); + if (irq < 0) { + fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + phb->msi_table[ndev].irq = irq; + phb->msi_table[ndev].nvec = req_num; + phb->msi_table[ndev].config_addr = config_addr; + } + + /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ + spapr_msi_setmsg(pdev, phb->msi_win_addr | (ndev << 16), + ret_intr_type == RTAS_TYPE_MSIX, req_num); + + rtas_st(rets, 0, 0); + rtas_st(rets, 1, req_num); + rtas_st(rets, 2, ++seq_num); + rtas_st(rets, 3, ret_intr_type); + + trace_spapr_pci_rtas_ibm_change_msi(func, req_num); +} + +static void rtas_ibm_query_interrupt_source_number(sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, + target_ulong args, + uint32_t nret, + target_ulong rets) +{ + uint32_t config_addr = rtas_ld(args, 0); + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3); + int ndev; + sPAPRPHBState *phb = NULL; + + /* Fins sPAPRPHBState */ + phb = find_phb(spapr, buid); + if (!phb) { + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Find device descriptor and start IRQ */ + ndev = spapr_msicfg_find(phb, config_addr, false); + if (ndev < 0) { + trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + + intr_src_num = phb->msi_table[ndev].irq + ioa_intr_num; + trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num, + intr_src_num); + + rtas_st(rets, 0, 0); + rtas_st(rets, 1, intr_src_num); + rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ +} + static int pci_spapr_swizzle(int slot, int pin) { return (slot + pin) % PCI_NUM_PINS; @@ -223,7 +435,8 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) */ sPAPRPHBState *phb = opaque; - qemu_set_irq(phb->lsi_table[irq_num].qirq, level); + trace_spapr_pci_lsi_set(phb->busname, irq_num, phb->lsi_table[irq_num].irq); + qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr, @@ -264,6 +477,33 @@ static const MemoryRegionOps spapr_io_ops = { }; /* + * MSI/MSIX memory region implementation. + * The handler handles both MSI and MSIX. + * For MSI-X, the vector number is encoded as a part of the address, + * data is set to 0. + * For MSI, the vector number is encoded in least bits in data. + */ +static void spapr_msi_write(void *opaque, target_phys_addr_t addr, + uint64_t data, unsigned size) +{ + sPAPRPHBState *phb = opaque; + int ndev = addr >> 16; + int vec = ((addr & 0xFFFF) >> 2) | data; + uint32_t irq = phb->msi_table[ndev].irq + vec; + + trace_spapr_pci_msi_write(addr, data, irq); + + qemu_irq_pulse(xics_get_qirq(spapr->icp, irq)); +} + +static const MemoryRegionOps spapr_msi_ops = { + /* There is no .read as the read result is undefined by PCI spec */ + .read = NULL, + .write = spapr_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +/* * PHB PCI device */ static DMAContext *spapr_pci_dma_context_fn(PCIBus *bus, void *opaque, @@ -276,24 +516,24 @@ static DMAContext *spapr_pci_dma_context_fn(PCIBus *bus, void *opaque, static int spapr_phb_init(SysBusDevice *s) { - sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s); + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s); char *namebuf; int i; PCIBus *bus; - uint32_t liobn; - phb->dtbusname = g_strdup_printf("pci@%" PRIx64, phb->buid); - namebuf = alloca(strlen(phb->dtbusname) + 32); + sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); + namebuf = alloca(strlen(sphb->dtbusname) + 32); /* Initialize memory regions */ - sprintf(namebuf, "%s.mmio", phb->dtbusname); - memory_region_init(&phb->memspace, namebuf, INT64_MAX); + sprintf(namebuf, "%s.mmio", sphb->dtbusname); + memory_region_init(&sphb->memspace, namebuf, INT64_MAX); - sprintf(namebuf, "%s.mmio-alias", phb->dtbusname); - memory_region_init_alias(&phb->memwindow, namebuf, &phb->memspace, - SPAPR_PCI_MEM_WIN_BUS_OFFSET, phb->mem_win_size); - memory_region_add_subregion(get_system_memory(), phb->mem_win_addr, - &phb->memwindow); + sprintf(namebuf, "%s.mmio-alias", sphb->dtbusname); + memory_region_init_alias(&sphb->memwindow, namebuf, &sphb->memspace, + SPAPR_PCI_MEM_WIN_BUS_OFFSET, sphb->mem_win_size); + memory_region_add_subregion(get_system_memory(), sphb->mem_win_addr, + &sphb->memwindow); /* On ppc, we only have MMIO no specific IO space from the CPU * perspective. In theory we ought to be able to embed the PCI IO @@ -303,42 +543,53 @@ static int spapr_phb_init(SysBusDevice *s) * system io address space. This hack to bounce things via * system_io works around the problem until all the users of * old_portion are updated */ - sprintf(namebuf, "%s.io", phb->dtbusname); - memory_region_init(&phb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE); + sprintf(namebuf, "%s.io", sphb->dtbusname); + memory_region_init(&sphb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE); /* FIXME: fix to support multiple PHBs */ - memory_region_add_subregion(get_system_io(), 0, &phb->iospace); + memory_region_add_subregion(get_system_io(), 0, &sphb->iospace); - sprintf(namebuf, "%s.io-alias", phb->dtbusname); - memory_region_init_io(&phb->iowindow, &spapr_io_ops, phb, + sprintf(namebuf, "%s.io-alias", sphb->dtbusname); + memory_region_init_io(&sphb->iowindow, &spapr_io_ops, sphb, namebuf, SPAPR_PCI_IO_WIN_SIZE); - memory_region_add_subregion(get_system_memory(), phb->io_win_addr, - &phb->iowindow); + memory_region_add_subregion(get_system_memory(), sphb->io_win_addr, + &sphb->iowindow); + + /* As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, + * we need to allocate some memory to catch those writes coming + * from msi_notify()/msix_notify() */ + if (msi_supported) { + sprintf(namebuf, "%s.msi", sphb->dtbusname); + memory_region_init_io(&sphb->msiwindow, &spapr_msi_ops, sphb, + namebuf, SPAPR_MSIX_MAX_DEVS * 0x10000); + memory_region_add_subregion(get_system_memory(), sphb->msi_win_addr, + &sphb->msiwindow); + } - bus = pci_register_bus(&phb->busdev.qdev, - phb->busname ? phb->busname : phb->dtbusname, - pci_spapr_set_irq, pci_spapr_map_irq, phb, - &phb->memspace, &phb->iospace, + bus = pci_register_bus(DEVICE(s), + sphb->busname ? sphb->busname : sphb->dtbusname, + pci_spapr_set_irq, pci_spapr_map_irq, sphb, + &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS); - phb->host_state.bus = bus; + phb->bus = bus; - liobn = SPAPR_PCI_BASE_LIOBN | (pci_find_domain(bus) << 16); - phb->dma = spapr_tce_new_dma_context(liobn, 0x40000000); - pci_setup_iommu(bus, spapr_pci_dma_context_fn, phb); + sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN | (pci_find_domain(bus) << 16); + sphb->dma_window_start = 0; + sphb->dma_window_size = 0x40000000; + sphb->dma = spapr_tce_new_dma_context(sphb->dma_liobn, sphb->dma_window_size); + pci_setup_iommu(bus, spapr_pci_dma_context_fn, sphb); - QLIST_INSERT_HEAD(&spapr->phbs, phb, list); + QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); /* Initialize the LSI table */ for (i = 0; i < PCI_NUM_PINS; i++) { - qemu_irq qirq; - uint32_t num; + uint32_t irq; - qirq = spapr_allocate_lsi(0, &num); - if (!qirq) { + irq = spapr_allocate_lsi(0); + if (!irq) { return -1; } - phb->lsi_table[i].dt_irq = num; - phb->lsi_table[i].qirq = qirq; + sphb->lsi_table[i].irq = irq; } return 0; @@ -351,6 +602,7 @@ static Property spapr_phb_properties[] = { DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, 0x20000000), DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, 0), DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, 0x10000), + DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -361,16 +613,11 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) sdc->init = spapr_phb_init; dc->props = spapr_phb_properties; - - spapr_rtas_register("read-pci-config", rtas_read_pci_config); - spapr_rtas_register("write-pci-config", rtas_write_pci_config); - spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); - spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); } -static TypeInfo spapr_phb_info = { - .name = "spapr-pci-host-bridge", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo spapr_phb_info = { + .name = TYPE_SPAPR_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(sPAPRPHBState), .class_init = spapr_phb_class_init, }; @@ -378,11 +625,11 @@ static TypeInfo spapr_phb_info = { void spapr_create_phb(sPAPREnvironment *spapr, const char *busname, uint64_t buid, uint64_t mem_win_addr, uint64_t mem_win_size, - uint64_t io_win_addr) + uint64_t io_win_addr, uint64_t msi_win_addr) { DeviceState *dev; - dev = qdev_create(NULL, spapr_phb_info.name); + dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); if (busname) { qdev_prop_set_string(dev, "busname", g_strdup(busname)); @@ -391,6 +638,7 @@ void spapr_create_phb(sPAPREnvironment *spapr, qdev_prop_set_uint64(dev, "mem_win_addr", mem_win_addr); qdev_prop_set_uint64(dev, "mem_win_size", mem_win_size); qdev_prop_set_uint64(dev, "io_win_addr", io_win_addr); + qdev_prop_set_uint64(dev, "msi_win_addr", msi_win_addr); qdev_init_nofail(dev); } @@ -406,9 +654,9 @@ void spapr_create_phb(sPAPREnvironment *spapr, #define b_fff(x) b_x((x), 8, 3) /* function number */ #define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ -int spapr_populate_pci_devices(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt) +int spapr_populate_pci_dt(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt) { int bus_off, i, j; char nodename[256]; @@ -477,7 +725,7 @@ int spapr_populate_pci_devices(sPAPRPHBState *phb, irqmap[2] = 0; irqmap[3] = cpu_to_be32(j+1); irqmap[4] = cpu_to_be32(xics_phandle); - irqmap[5] = cpu_to_be32(phb->lsi_table[lsi_num].dt_irq); + irqmap[5] = cpu_to_be32(phb->lsi_table[lsi_num].irq); irqmap[6] = cpu_to_be32(0x8); } } @@ -485,13 +733,29 @@ int spapr_populate_pci_devices(sPAPRPHBState *phb, _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, sizeof(interrupt_map))); - spapr_dma_dt(fdt, bus_off, "ibm,dma-window", phb->dma); + spapr_dma_dt(fdt, bus_off, "ibm,dma-window", + phb->dma_liobn, phb->dma_window_start, + phb->dma_window_size); return 0; } -static void register_types(void) +void spapr_pci_rtas_init(void) +{ + spapr_rtas_register("read-pci-config", rtas_read_pci_config); + spapr_rtas_register("write-pci-config", rtas_write_pci_config); + spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); + spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); + if (msi_supported) { + spapr_rtas_register("ibm,query-interrupt-source-number", + rtas_ibm_query_interrupt_source_number); + spapr_rtas_register("ibm,change-msi", rtas_ibm_change_msi); + } +} + +static void spapr_pci_register_types(void) { type_register_static(&spapr_phb_info); } -type_init(register_types) + +type_init(spapr_pci_register_types) diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index d9e46e2..670dc62 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -27,9 +27,15 @@ #include "hw/pci_host.h" #include "hw/xics.h" +#define SPAPR_MSIX_MAX_DEVS 32 + +#define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge" + +#define SPAPR_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(sPAPRPHBState, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) + typedef struct sPAPRPHBState { - SysBusDevice busdev; - PCIHostState host_state; + PCIHostState parent_obj; uint64_t buid; char *busname; @@ -37,27 +43,44 @@ typedef struct sPAPRPHBState { MemoryRegion memspace, iospace; target_phys_addr_t mem_win_addr, mem_win_size, io_win_addr, io_win_size; - MemoryRegion memwindow, iowindow; + target_phys_addr_t msi_win_addr; + MemoryRegion memwindow, iowindow, msiwindow; + + uint32_t dma_liobn; + uint64_t dma_window_start; + uint64_t dma_window_size; DMAContext *dma; struct { - uint32_t dt_irq; - qemu_irq qirq; + uint32_t irq; } lsi_table[PCI_NUM_PINS]; + struct { + uint32_t config_addr; + uint32_t irq; + int nvec; + } msi_table[SPAPR_MSIX_MAX_DEVS]; + QLIST_ENTRY(sPAPRPHBState) list; } sPAPRPHBState; +static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) +{ + return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); +} + #define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL #define SPAPR_PCI_IO_WIN_SIZE 0x10000 void spapr_create_phb(sPAPREnvironment *spapr, const char *busname, uint64_t buid, uint64_t mem_win_addr, uint64_t mem_win_size, - uint64_t io_win_addr); + uint64_t io_win_addr, uint64_t msi_win_addr); + +int spapr_populate_pci_dt(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt); -int spapr_populate_pci_devices(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt); +void spapr_pci_rtas_init(void); #endif /* __HW_SPAPR_PCI_H__ */ diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index 05b5503..7ca4452 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -49,7 +49,7 @@ #endif static Property spapr_vio_props[] = { - DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, vio_irq_num, 0), \ + DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \ DEFINE_PROP_END_OF_LIST(), }; @@ -132,8 +132,8 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, } } - if (dev->qirq) { - uint32_t ints_prop[] = {cpu_to_be32(dev->vio_irq_num), 0}; + if (dev->irq) { + uint32_t ints_prop[] = {cpu_to_be32(dev->irq), 0}; ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop, sizeof(ints_prop)); @@ -142,7 +142,7 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, } } - ret = spapr_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->dma); + ret = spapr_tcet_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->dma); if (ret < 0) { return ret; } @@ -306,7 +306,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize; if (dev->signal_state & 1) { - qemu_irq_pulse(dev->qirq); + qemu_irq_pulse(spapr_vio_qirq(dev)); } return 0; @@ -459,8 +459,8 @@ static int spapr_vio_busdev_init(DeviceState *qdev) dev->qdev.id = id; } - dev->qirq = spapr_allocate_msi(dev->vio_irq_num, &dev->vio_irq_num); - if (!dev->qirq) { + dev->irq = spapr_allocate_msi(dev->irq); + if (!dev->irq) { return -1; } diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index 6f9a498..ea6aa43 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -61,8 +61,7 @@ struct VIOsPAPRDevice { DeviceState qdev; uint32_t reg; uint32_t flags; - qemu_irq qirq; - uint32_t vio_irq_num; + uint32_t irq; target_ulong signal_state; VIOsPAPR_CRQ crq; DMAContext *dma; @@ -85,6 +84,11 @@ extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus); extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode); +static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev) +{ + return xics_get_qirq(spapr->icp, dev->irq); +} + static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size, DMADirection dir) { diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index 99e52cc..5da17a3 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -26,7 +26,7 @@ static void vty_receive(void *opaque, const uint8_t *buf, int size) if ((dev->in == dev->out) && size) { /* toggle line to simulate edge interrupt */ - qemu_irq_pulse(dev->sdev.qirq); + qemu_irq_pulse(spapr_vio_qirq(&dev->sdev)); } for (i = 0; i < size; i++) { assert((dev->in - dev->out) < VTERM_BUFSIZE); diff --git a/hw/ssd0323.c b/hw/ssd0323.c index b0b2e94..b101c51 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -19,7 +19,9 @@ #define DPRINTF(fmt, ...) \ do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0) #define BADF(fmt, ...) \ -do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) +do { \ + fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \ +} while (0) #else #define DPRINTF(fmt, ...) do {} while(0) #define BADF(fmt, ...) \ diff --git a/hw/stream.c b/hw/stream.c new file mode 100644 index 0000000..be57e8b --- /dev/null +++ b/hw/stream.c @@ -0,0 +1,23 @@ +#include "stream.h" + +void +stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app) +{ + StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); + + k->push(sink, buf, len, app); +} + +static TypeInfo stream_slave_info = { + .name = TYPE_STREAM_SLAVE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(StreamSlaveClass), +}; + + +static void stream_slave_register_types(void) +{ + type_register_static(&stream_slave_info); +} + +type_init(stream_slave_register_types) diff --git a/hw/stream.h b/hw/stream.h new file mode 100644 index 0000000..21123a9 --- /dev/null +++ b/hw/stream.h @@ -0,0 +1,31 @@ +#ifndef STREAM_H +#define STREAM_H 1 + +#include "qemu-common.h" +#include "qemu/object.h" + +/* stream slave. Used until qdev provides a generic way. */ +#define TYPE_STREAM_SLAVE "stream-slave" + +#define STREAM_SLAVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(StreamSlaveClass, (klass), TYPE_STREAM_SLAVE) +#define STREAM_SLAVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(StreamSlaveClass, (obj), TYPE_STREAM_SLAVE) +#define STREAM_SLAVE(obj) \ + INTERFACE_CHECK(StreamSlave, (obj), TYPE_STREAM_SLAVE) + +typedef struct StreamSlave { + Object Parent; +} StreamSlave; + +typedef struct StreamSlaveClass { + InterfaceClass parent; + + void (*push)(StreamSlave *obj, unsigned char *buf, size_t len, + uint32_t *app); +} StreamSlaveClass; + +void +stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app); + +#endif /* STREAM_H */ @@ -39,6 +39,7 @@ #include "elf.h" #include "blockdev.h" #include "exec-memory.h" +#include "vga-pci.h" //#define DEBUG_IRQ //#define DEBUG_EBUS diff --git a/hw/unicore32/Makefile.objs b/hw/unicore32/Makefile.objs new file mode 100644 index 0000000..0725ce3 --- /dev/null +++ b/hw/unicore32/Makefile.objs @@ -0,0 +1,6 @@ +# For UniCore32 machines and boards + +# PKUnity-v3 SoC and board information +obj-${CONFIG_PUV3} += puv3.o + +obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/unin_pci.c b/hw/unin_pci.c index 409bcd4..d1cc680 100644 --- a/hw/unin_pci.c +++ b/hw/unin_pci.c @@ -38,8 +38,23 @@ static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; +#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" +#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" +#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" +#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" + +#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) +#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) +#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) +#define U3_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) + typedef struct UNINState { - PCIHostState host_state; + PCIHostState parent_obj; + MemoryRegion pci_mmio; MemoryRegion pci_hole; } UNINState; @@ -100,10 +115,11 @@ static void unin_data_write(void *opaque, target_phys_addr_t addr, uint64_t val, unsigned len) { UNINState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n", addr, len, val); - pci_data_write(s->host_state.bus, - unin_get_config_reg(s->host_state.config_reg, addr), + pci_data_write(phb->bus, + unin_get_config_reg(phb->config_reg, addr), val, len); } @@ -111,10 +127,11 @@ static uint64_t unin_data_read(void *opaque, target_phys_addr_t addr, unsigned len) { UNINState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); uint32_t val; - val = pci_data_read(s->host_state.bus, - unin_get_config_reg(s->host_state.config_reg, addr), + val = pci_data_read(phb->bus, + unin_get_config_reg(phb->config_reg, addr), len); UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n", addr, len, val); @@ -130,19 +147,17 @@ static const MemoryRegionOps unin_data_ops = { static int pci_unin_main_init_device(SysBusDevice *dev) { PCIHostState *h; - UNINState *s; /* Use values found on a real PowerMac */ /* Uninorth main bus */ - h = FROM_SYSBUS(PCIHostState, dev); - s = DO_UPCAST(UNINState, host_state, h); + h = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&s->host_state.conf_mem, &pci_host_conf_le_ops, - &s->host_state, "pci-conf-idx", 0x1000); - memory_region_init_io(&s->host_state.data_mem, &unin_data_ops, s, + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &unin_data_ops, dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &s->host_state.conf_mem); - sysbus_init_mmio(dev, &s->host_state.data_mem); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); return 0; } @@ -151,18 +166,16 @@ static int pci_unin_main_init_device(SysBusDevice *dev) static int pci_u3_agp_init_device(SysBusDevice *dev) { PCIHostState *h; - UNINState *s; /* Uninorth U3 AGP bus */ - h = FROM_SYSBUS(PCIHostState, dev); - s = DO_UPCAST(UNINState, host_state, h); + h = PCI_HOST_BRIDGE(dev); - memory_region_init_io(&s->host_state.conf_mem, &pci_host_conf_le_ops, - &s->host_state, "pci-conf-idx", 0x1000); - memory_region_init_io(&s->host_state.data_mem, &unin_data_ops, s, + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &unin_data_ops, dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &s->host_state.conf_mem); - sysbus_init_mmio(dev, &s->host_state.data_mem); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); return 0; } @@ -170,36 +183,32 @@ static int pci_u3_agp_init_device(SysBusDevice *dev) static int pci_unin_agp_init_device(SysBusDevice *dev) { PCIHostState *h; - UNINState *s; /* Uninorth AGP bus */ - h = FROM_SYSBUS(PCIHostState, dev); - s = DO_UPCAST(UNINState, host_state, h); - - memory_region_init_io(&s->host_state.conf_mem, &pci_host_conf_le_ops, - &s->host_state, "pci-conf-idx", 0x1000); - memory_region_init_io(&s->host_state.data_mem, &pci_host_data_le_ops, - &s->host_state, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &s->host_state.conf_mem); - sysbus_init_mmio(dev, &s->host_state.data_mem); + h = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, + dev, "pci-conf-data", 0x1000); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); return 0; } static int pci_unin_internal_init_device(SysBusDevice *dev) { PCIHostState *h; - UNINState *s; /* Uninorth internal bus */ - h = FROM_SYSBUS(PCIHostState, dev); - s = DO_UPCAST(UNINState, host_state, h); - - memory_region_init_io(&s->host_state.conf_mem, &pci_host_conf_le_ops, - &s->host_state, "pci-conf-idx", 0x1000); - memory_region_init_io(&s->host_state.data_mem, &pci_host_data_le_ops, - &s->host_state, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &s->host_state.conf_mem); - sysbus_init_mmio(dev, &s->host_state.data_mem); + h = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, + dev, "pci-conf-data", 0x1000); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); return 0; } @@ -214,26 +223,26 @@ PCIBus *pci_pmac_init(qemu_irq *pic, /* Use values found on a real PowerMac */ /* Uninorth main bus */ - dev = qdev_create(NULL, "uni-north-pci-pcihost"); + dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); - h = FROM_SYSBUS(PCIHostState, s); - d = DO_UPCAST(UNINState, host_state, h); + s = SYS_BUS_DEVICE(dev); + h = PCI_HOST_BRIDGE(s); + d = UNI_NORTH_PCI_HOST_BRIDGE(dev); memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, 0x80000000ULL, 0x70000000ULL); memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - d->host_state.bus = pci_register_bus(dev, "pci", - pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4); + h->bus = pci_register_bus(dev, "pci", + pci_unin_set_irq, pci_unin_map_irq, + pic, + &d->pci_mmio, + address_space_io, + PCI_DEVFN(11, 0), 4); #if 0 - pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north"); + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north"); #endif sysbus_mmio_map(s, 0, 0xf2800000); @@ -242,30 +251,30 @@ PCIBus *pci_pmac_init(qemu_irq *pic, /* DEC 21154 bridge */ #if 0 /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ - pci_create_simple(d->host_state.bus, PCI_DEVFN(12, 0), "dec-21154"); + pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); #endif /* Uninorth AGP bus */ - pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north-agp"); - dev = qdev_create(NULL, "uni-north-agp-pcihost"); + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); + dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0xf0800000); sysbus_mmio_map(s, 1, 0xf0c00000); /* Uninorth internal bus */ #if 0 /* XXX: not needed for now */ - pci_create_simple(d->host_state.bus, PCI_DEVFN(14, 0), + pci_create_simple(h->bus, PCI_DEVFN(14, 0), "uni-north-internal-pci"); - dev = qdev_create(NULL, "uni-north-internal-pci-pcihost"); + dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0xf4800000); sysbus_mmio_map(s, 1, 0xf4c00000); #endif - return d->host_state.bus; + return h->bus; } PCIBus *pci_pmac_u3_init(qemu_irq *pic, @@ -279,11 +288,11 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic, /* Uninorth AGP bus */ - dev = qdev_create(NULL, "u3-agp-pcihost"); + dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); - h = FROM_SYSBUS(PCIHostState, s); - d = DO_UPCAST(UNINState, host_state, h); + s = SYS_BUS_DEVICE(dev); + h = PCI_HOST_BRIDGE(dev); + d = U3_AGP_HOST_BRIDGE(dev); memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, @@ -291,19 +300,19 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic, memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - d->host_state.bus = pci_register_bus(dev, "pci", - pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4); + h->bus = pci_register_bus(dev, "pci", + pci_unin_set_irq, pci_unin_map_irq, + pic, + &d->pci_mmio, + address_space_io, + PCI_DEVFN(11, 0), 4); sysbus_mmio_map(s, 0, 0xf0800000); sysbus_mmio_map(s, 1, 0xf0c00000); - pci_create_simple(d->host_state.bus, 11 << 3, "u3-agp"); + pci_create_simple(h->bus, 11 << 3, "u3-agp"); - return d->host_state.bus; + return h->bus; } static int unin_main_pci_host_init(PCIDevice *d) @@ -350,7 +359,7 @@ static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; } -static TypeInfo unin_main_pci_host_info = { +static const TypeInfo unin_main_pci_host_info = { .name = "uni-north-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -368,7 +377,7 @@ static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; } -static TypeInfo u3_agp_pci_host_info = { +static const TypeInfo u3_agp_pci_host_info = { .name = "u3-agp", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -386,7 +395,7 @@ static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; } -static TypeInfo unin_agp_pci_host_info = { +static const TypeInfo unin_agp_pci_host_info = { .name = "uni-north-agp", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -404,7 +413,7 @@ static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; } -static TypeInfo unin_internal_pci_host_info = { +static const TypeInfo unin_internal_pci_host_info = { .name = "uni-north-internal-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -418,9 +427,9 @@ static void pci_unin_main_class_init(ObjectClass *klass, void *data) sbc->init = pci_unin_main_init_device; } -static TypeInfo pci_unin_main_info = { - .name = "uni-north-pci-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo pci_unin_main_info = { + .name = TYPE_UNI_NORTH_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), .class_init = pci_unin_main_class_init, }; @@ -432,9 +441,9 @@ static void pci_u3_agp_class_init(ObjectClass *klass, void *data) sbc->init = pci_u3_agp_init_device; } -static TypeInfo pci_u3_agp_info = { - .name = "u3-agp-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo pci_u3_agp_info = { + .name = TYPE_U3_AGP_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), .class_init = pci_u3_agp_class_init, }; @@ -446,9 +455,9 @@ static void pci_unin_agp_class_init(ObjectClass *klass, void *data) sbc->init = pci_unin_agp_init_device; } -static TypeInfo pci_unin_agp_info = { - .name = "uni-north-agp-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo pci_unin_agp_info = { + .name = TYPE_UNI_NORTH_AGP_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), .class_init = pci_unin_agp_class_init, }; @@ -460,9 +469,9 @@ static void pci_unin_internal_class_init(ObjectClass *klass, void *data) sbc->init = pci_unin_internal_init_device; } -static TypeInfo pci_unin_internal_info = { - .name = "uni-north-internal-pci-pcihost", - .parent = TYPE_SYS_BUS_DEVICE, +static const TypeInfo pci_unin_internal_info = { + .name = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(UNINState), .class_init = pci_unin_internal_class_init, }; @@ -179,6 +179,7 @@ struct USBEndpoint { uint8_t ifnum; int max_packet_size; bool pipeline; + bool halted; USBDevice *dev; QTAILQ_HEAD(, USBPacket) queue; }; @@ -331,6 +332,7 @@ typedef enum USBPacketState { struct USBPacket { /* Data fields for use by the driver. */ int pid; + uint64_t id; USBEndpoint *ep; QEMUIOVector iov; uint64_t parameter; /* control transfers */ @@ -343,7 +345,7 @@ struct USBPacket { void usb_packet_init(USBPacket *p); void usb_packet_set_state(USBPacket *p, USBPacketState state); void usb_packet_check_state(USBPacket *p, USBPacketState expected); -void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep); +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl); diff --git a/hw/usb/core.c b/hw/usb/core.c index 01a7622..2da38e7 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -107,6 +107,7 @@ static int do_token_setup(USBDevice *s, USBPacket *p) } usb_packet_copy(p, s->setup_buf, p->iov.size); + p->result = 0; s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_index = 0; @@ -381,12 +382,23 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) usb_packet_check_state(p, USB_PACKET_SETUP); assert(p->ep != NULL); + /* Submitting a new packet clears halt */ + if (p->ep->halted) { + assert(QTAILQ_EMPTY(&p->ep->queue)); + p->ep->halted = false; + } + if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) { ret = usb_process_one(p); if (ret == USB_RET_ASYNC) { usb_packet_set_state(p, USB_PACKET_ASYNC); QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); } else { + /* + * When pipelining is enabled usb-devices must always return async, + * otherwise packets can complete out of order! + */ + assert(!p->ep->pipeline); p->result = ret; usb_packet_set_state(p, USB_PACKET_COMPLETE); } @@ -398,6 +410,20 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) return ret; } +static void __usb_packet_complete(USBDevice *dev, USBPacket *p) +{ + USBEndpoint *ep = p->ep; + + assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK); + + if (p->result < 0) { + ep->halted = true; + } + usb_packet_set_state(p, USB_PACKET_COMPLETE); + QTAILQ_REMOVE(&ep->queue, p, queue); + dev->port->ops->complete(dev->port, p); +} + /* Notify the controller that an async packet is complete. This should only be called for packets previously deferred by returning USB_RET_ASYNC from handle_packet. */ @@ -408,11 +434,9 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) usb_packet_check_state(p, USB_PACKET_ASYNC); assert(QTAILQ_FIRST(&ep->queue) == p); - usb_packet_set_state(p, USB_PACKET_COMPLETE); - QTAILQ_REMOVE(&ep->queue, p, queue); - dev->port->ops->complete(dev->port, p); + __usb_packet_complete(dev, p); - while (!QTAILQ_EMPTY(&ep->queue)) { + while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) { p = QTAILQ_FIRST(&ep->queue); if (p->state == USB_PACKET_ASYNC) { break; @@ -424,9 +448,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) break; } p->result = ret; - usb_packet_set_state(p, USB_PACKET_COMPLETE); - QTAILQ_REMOVE(&ep->queue, p, queue); - dev->port->ops->complete(dev->port, p); + __usb_packet_complete(ep->dev, p); } } @@ -498,10 +520,11 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state) p->state = state; } -void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep) +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id) { assert(!usb_packet_is_inflight(p)); assert(p->iov.iov != NULL); + p->id = id; p->pid = pid; p->ep = ep; p->result = 0; diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 9b02ff4..5a0057a 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -223,7 +223,7 @@ static const USBDescDevice desc_device_high = { static const USBDesc desc = { .id = { .idVendor = 0x46f4, /* CRC16() of "QEMU" */ - .idProduct = 0x0002, + .idProduct = 0x0003, .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT, @@ -424,6 +424,7 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv) } QTAILQ_REMOVE(&uas->requests, req, next); g_free(req); + usb_uas_start_next_transfer(uas); } static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag) @@ -456,7 +457,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r, uint32_t status, size_t resid) { UASRequest *req = r->hba_private; - UASDevice *uas = req->uas; trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid); req->complete = true; @@ -465,7 +465,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r, } usb_uas_queue_sense(req, status); scsi_req_unref(req->req); - usb_uas_start_next_transfer(uas); } static void usb_uas_scsi_request_cancelled(SCSIRequest *r) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index b043e7c..017342b 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -575,7 +575,12 @@ static inline void ehci_update_irq(EHCIState *s) /* flag interrupt condition */ static inline void ehci_raise_irq(EHCIState *s, int intr) { - s->usbsts_pending |= intr; + if (intr & (USBSTS_PCD | USBSTS_FLR | USBSTS_HSE)) { + s->usbsts |= intr; + ehci_update_irq(s); + } else { + s->usbsts_pending |= intr; + } } /* @@ -761,15 +766,27 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) return q; } +static void ehci_cancel_queue(EHCIQueue *q) +{ + EHCIPacket *p; + + p = QTAILQ_FIRST(&q->packets); + if (p == NULL) { + return; + } + + trace_usb_ehci_queue_action(q, "cancel"); + do { + ehci_free_packet(p); + } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); +} + static void ehci_free_queue(EHCIQueue *q) { EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; - EHCIPacket *p; trace_usb_ehci_queue_action(q, "free"); - while ((p = QTAILQ_FIRST(&q->packets)) != NULL) { - ehci_free_packet(p); - } + ehci_cancel_queue(q); QTAILQ_REMOVE(head, q, next); g_free(q); } @@ -1182,22 +1199,32 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) break; } + /* not supporting dynamic frame list size at the moment */ + if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { + fprintf(stderr, "attempt to set frame list size -- value %d\n", + val & USBCMD_FLS); + val &= ~USBCMD_FLS; + } + + if (val & USBCMD_IAAD) { + /* + * Process IAAD immediately, otherwise the Linux IAAD watchdog may + * trigger and re-use a qh without us seeing the unlink. + */ + s->async_stepdown = 0; + qemu_bh_schedule(s->async_bh); + } + if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) != ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) { if (s->pstate == EST_INACTIVE) { SET_LAST_RUN_CLOCK(s); } + s->usbcmd = val; /* Set usbcmd for ehci_update_halt() */ ehci_update_halt(s); s->async_stepdown = 0; qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); } - - /* not supporting dynamic frame list size at the moment */ - if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { - fprintf(stderr, "attempt to set frame list size -- value %d\n", - val & USBCMD_FLS); - val &= ~USBCMD_FLS; - } break; case USBSTS: @@ -1524,7 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action) endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); ep = usb_ep_get(p->queue->dev, p->pid, endp); - usb_packet_setup(&p->packet, p->pid, ep); + usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr); usb_packet_map(&p->packet, &p->sgl); trace_usb_ehci_packet_action(p->queue, p, action); @@ -1546,7 +1573,8 @@ static int ehci_execute(EHCIPacket *p, const char *action) */ static int ehci_process_itd(EHCIState *ehci, - EHCIitd *itd) + EHCIitd *itd, + uint32_t addr) { USBDevice *dev; USBEndpoint *ep; @@ -1591,8 +1619,8 @@ static int ehci_process_itd(EHCIState *ehci, dev = ehci_find_device(ehci, devaddr); ep = usb_ep_get(dev, pid, endp); - if (ep->type == USB_ENDPOINT_XFER_ISOC) { - usb_packet_setup(&ehci->ipacket, pid, ep); + if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { + usb_packet_setup(&ehci->ipacket, pid, ep, addr); usb_packet_map(&ehci->ipacket, &ehci->isgl); ret = usb_handle_packet(dev, &ehci->ipacket); assert(ret != USB_RET_ASYNC); @@ -1780,9 +1808,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) if (q->dev != NULL && q->dev->addr != devaddr) { if (!QTAILQ_EMPTY(&q->packets)) { /* should not happen (guest bug) */ - while ((p = QTAILQ_FIRST(&q->packets)) != NULL) { - ehci_free_packet(p); - } + ehci_cancel_queue(q); } q->dev = NULL; } @@ -1790,11 +1816,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) q->dev = ehci_find_device(q->ehci, devaddr); } - if (p && p->async == EHCI_ASYNC_INFLIGHT) { - /* I/O still in progress -- skip queue */ - ehci_set_state(ehci, async, EST_HORIZONTALQH); - goto out; - } if (p && p->async == EHCI_ASYNC_FINISHED) { /* I/O finished -- continue processing queue */ trace_usb_ehci_packet_action(p->queue, p, "complete"); @@ -1856,7 +1877,7 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) sizeof(EHCIitd) >> 2); ehci_trace_itd(ehci, entry, &itd); - if (ehci_process_itd(ehci, &itd) != 0) { + if (ehci_process_itd(ehci, &itd, entry) != 0) { return -1; } @@ -1943,29 +1964,50 @@ static int ehci_state_fetchqtd(EHCIQueue *q) ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); p = QTAILQ_FIRST(&q->packets); - while (p != NULL && p->qtdaddr != q->qtdaddr) { - /* should not happen (guest bug) */ - ehci_free_packet(p); - p = QTAILQ_FIRST(&q->packets); - } if (p != NULL) { - ehci_qh_do_overlay(q); - ehci_flush_qh(q); - if (p->async == EHCI_ASYNC_INFLIGHT) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); + if (p->qtdaddr != q->qtdaddr || + (!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) || + (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) || + p->qtd.bufptr[0] != qtd.bufptr[0]) { + /* guest bug: guest updated active QH or qTD underneath us */ + ehci_cancel_queue(q); + p = NULL; } else { + p->qtd = qtd; + ehci_qh_do_overlay(q); + } + } + + if (!(qtd.token & QTD_TOKEN_ACTIVE)) { + if (p != NULL) { + /* transfer canceled by guest (clear active) */ + ehci_cancel_queue(q); + p = NULL; + } + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); + again = 1; + } else if (p != NULL) { + switch (p->async) { + case EHCI_ASYNC_NONE: + /* Previously nacked packet (likely interrupt ep) */ + ehci_set_state(q->ehci, q->async, EST_EXECUTE); + break; + case EHCI_ASYNC_INFLIGHT: + /* Unfinyshed async handled packet, go horizontal */ + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); + break; + case EHCI_ASYNC_FINISHED: + /* Should never happen, as this case is caught by fetchqh */ ehci_set_state(q->ehci, q->async, EST_EXECUTING); + break; } again = 1; - } else if (qtd.token & QTD_TOKEN_ACTIVE) { + } else { p = ehci_alloc_packet(q); p->qtdaddr = q->qtdaddr; p->qtd = qtd; ehci_set_state(q->ehci, q->async, EST_EXECUTE); again = 1; - } else { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; } return again; @@ -2010,7 +2052,7 @@ static void ehci_fill_queue(EHCIPacket *p) p->qtdaddr = qtdaddr; p->qtd = qtd; p->usb_status = ehci_execute(p, "queue"); - assert(p->usb_status = USB_RET_ASYNC); + assert(p->usb_status == USB_RET_ASYNC); p->async = EHCI_ASYNC_INFLIGHT; } } @@ -2069,19 +2111,11 @@ out: static int ehci_state_executing(EHCIQueue *q) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); - int again = 0; assert(p != NULL); assert(p->qtdaddr == q->qtdaddr); ehci_execute_complete(q); - if (p->usb_status == USB_RET_ASYNC) { - goto out; - } - if (p->usb_status == USB_RET_PROCERR) { - again = -1; - goto out; - } // 4.10.3 if (!q->async) { @@ -2099,11 +2133,8 @@ static int ehci_state_executing(EHCIQueue *q) ehci_set_state(q->ehci, q->async, EST_WRITEBACK); } - again = 1; - -out: ehci_flush_qh(q); - return again; + return 1; } @@ -2132,6 +2163,19 @@ static int ehci_state_writeback(EHCIQueue *q) * bit is clear. */ if (q->qh.token & QTD_TOKEN_HALT) { + /* + * We should not do any further processing on a halted queue! + * This is esp. important for bulk endpoints with pipelining enabled + * (redirection to a real USB device), where we must cancel all the + * transfers after this one so that: + * 1) If they've completed already, they are not processed further + * causing more stalls, originating from the same failed transfer + * 2) If still in flight, they are cancelled before the guest does + * a clear stall, otherwise the guest and device can loose sync! + */ + while ((p = QTAILQ_FIRST(&q->packets)) != NULL) { + ehci_free_packet(p); + } ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); again = 1; } else { @@ -2466,13 +2510,16 @@ static int usb_ehci_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_ehci = { .name = "ehci", - .version_id = 1, + .version_id = 2, + .minimum_version_id = 1, .post_load = usb_ehci_post_load, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, EHCIState), /* mmio registers */ VMSTATE_UINT32(usbcmd, EHCIState), VMSTATE_UINT32(usbsts, EHCIState), + VMSTATE_UINT32_V(usbsts_pending, EHCIState, 2), + VMSTATE_UINT32_V(usbsts_frindex, EHCIState, 2), VMSTATE_UINT32(usbintr, EHCIState), VMSTATE_UINT32(frindex, EHCIState), VMSTATE_UINT32(ctrldssegment, EHCIState), diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c index 0e860da..e77e0b2 100644 --- a/hw/usb/hcd-musb.c +++ b/hw/usb/hcd-musb.c @@ -627,7 +627,8 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, /* A wild guess on the FADDR semantics... */ dev = usb_find_device(&s->port, ep->faddr[idx]); uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); - usb_packet_setup(&ep->packey[dir].p, pid, uep); + usb_packet_setup(&ep->packey[dir].p, pid, uep, + (dev->addr << 16) | (uep->nr << 8) | pid); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 844e7ed..c36184a 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -812,7 +812,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } else { dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep); + usb_packet_setup(&ohci->usb_packet, pid, ep, addr); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret == USB_RET_ASYNC) { @@ -1011,7 +1011,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep); + usb_packet_setup(&ohci->usb_packet, pid, ep, addr); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); ret = usb_handle_packet(dev, &ohci->usb_packet); #ifdef DEBUG_PACKET diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 1ace2a4..b0db921 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -748,6 +748,22 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ return TD_RESULT_COMPLETE; out: + /* + * We should not do any further processing on a queue with errors! + * This is esp. important for bulk endpoints with pipelining enabled + * (redirection to a real USB device), where we must cancel all the + * transfers after this one so that: + * 1) If they've completed already, they are not processed further + * causing more stalls, originating from the same failed transfer + * 2) If still in flight, they are cancelled before the guest does + * a clear stall, otherwise the guest and device can loose sync! + */ + while (!QTAILQ_EMPTY(&async->queue->asyncs)) { + UHCIAsync *as = QTAILQ_FIRST(&async->queue->asyncs); + uhci_async_unlink(as); + uhci_async_cancel(as); + } + switch(ret) { case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; @@ -843,14 +859,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, * for initial isochronous requests */ async->queue->valid = 32; - async->isoc = td->ctrl & TD_CTRL_IOS; + async->isoc = td->ctrl & TD_CTRL_IOS; max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; dev = uhci_find_device(s, (td->token >> 8) & 0x7f); ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); - usb_packet_setup(&async->packet, pid, ep); + usb_packet_setup(&async->packet, pid, ep, addr); qemu_sglist_add(&async->sgl, td->buffer, max_len); usb_packet_map(&async->packet, &async->sgl); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 6c2ff02..3eb27fa 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1392,7 +1392,7 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; ep = usb_ep_get(dev, dir, xfer->epid >> 1); - usb_packet_setup(&xfer->packet, dir, ep); + usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr); usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", xfer->packet.pid, dev->addr, ep->nr); diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c index d55be87..8df9207 100644 --- a/hw/usb/host-linux.c +++ b/hw/usb/host-linux.c @@ -1045,6 +1045,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, /* Note request is (bRequestType << 8) | bRequest */ trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index); + assert(p->result == 0); switch (request) { case DeviceOutRequest | USB_REQ_SET_ADDRESS: diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 4fd5d9b..7a92034 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -162,7 +162,7 @@ static int vpb_sic_init(SysBusDevice *dev) /* Board init. */ /* The AB and PB boards both use the same core, just with different - peripherans and expansion busses. For now we emulate a subset of the + peripherals and expansion busses. For now we emulate a subset of the PB peripherals and just change the board ID. */ static struct arm_boot_info versatile_binfo; diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 37dc019..9abbada 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -23,8 +23,8 @@ */ #include "hw.h" #include "console.h" -#include "pc.h" #include "pci.h" +#include "vga-pci.h" #include "vga_int.h" #include "pixel_ops.h" #include "qemu-timer.h" diff --git a/hw/vga-pci.h b/hw/vga-pci.h new file mode 100644 index 0000000..49abf13 --- /dev/null +++ b/hw/vga-pci.h @@ -0,0 +1,12 @@ +#ifndef VGA_PCI_H +#define VGA_PCI_H + +#include "qemu-common.h" + +/* vga-pci.c */ +DeviceState *pci_vga_init(PCIBus *bus); + +/* cirrus_vga.c */ +DeviceState *pci_cirrus_vga_init(PCIBus *bus); + +#endif diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index f21757e..6f6d172 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -254,6 +254,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) virtio_blk_req_complete(req, status); g_free(req); + return; #else abort(); #endif @@ -509,9 +510,19 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blkcfg.size_max = 0; blkcfg.physical_block_exp = get_physical_block_exp(s->conf); blkcfg.alignment_offset = 0; + blkcfg.wce = bdrv_enable_write_cache(s->bs); memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); } +static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VirtIOBlock *s = to_virtio_blk(vdev); + struct virtio_blk_config blkcfg; + + memcpy(&blkcfg, config, sizeof(blkcfg)); + bdrv_set_enable_write_cache(s->bs, blkcfg.wce != 0); +} + static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) { VirtIOBlock *s = to_virtio_blk(vdev); @@ -523,14 +534,27 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) features |= (1 << VIRTIO_BLK_F_SCSI); if (bdrv_enable_write_cache(s->bs)) - features |= (1 << VIRTIO_BLK_F_WCACHE); - + features |= (1 << VIRTIO_BLK_F_WCE); + if (bdrv_is_read_only(s->bs)) features |= 1 << VIRTIO_BLK_F_RO; return features; } +static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) +{ + VirtIOBlock *s = to_virtio_blk(vdev); + uint32_t features; + + if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return; + } + + features = vdev->guest_features; + bdrv_set_enable_write_cache(s->bs, !!(features & (1 << VIRTIO_BLK_F_WCE))); +} + static void virtio_blk_save(QEMUFile *f, void *opaque) { VirtIOBlock *s = opaque; @@ -609,7 +633,9 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) sizeof(VirtIOBlock)); s->vdev.get_config = virtio_blk_update_config; + s->vdev.set_config = virtio_blk_set_config; s->vdev.get_features = virtio_blk_get_features; + s->vdev.set_status = virtio_blk_set_status; s->vdev.reset = virtio_blk_reset; s->bs = blk->conf.bs; s->conf = &blk->conf; diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index 79ebccc..f0740d0 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -31,8 +31,9 @@ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ /* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */ -#define VIRTIO_BLK_F_WCACHE 9 /* write cache enabled */ +#define VIRTIO_BLK_F_WCE 9 /* write cache enabled */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ +#define VIRTIO_BLK_F_CONFIG_WCE 11 /* write cache configurable */ #define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ @@ -49,6 +50,7 @@ struct virtio_blk_config uint8_t alignment_offset; uint16_t min_io_size; uint32_t opt_io_size; + uint8_t wce; } QEMU_PACKED; /* These two define direction. */ @@ -102,9 +104,11 @@ struct VirtIOBlkConf BlockConf conf; char *serial; uint32_t scsi; + uint32_t config_wce; }; #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ - DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) + DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ + DEFINE_PROP_BIT("config-wce", _state, _field, VIRTIO_BLK_F_CONFIG_WCE, true) #endif diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 5e6e09e..b3f0710 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -131,6 +131,7 @@ static int virtio_pci_load_config(void * opaque, QEMUFile *f) if (ret) { return ret; } + msix_unuse_all_vectors(&proxy->pci_dev); msix_load(&proxy->pci_dev, f); if (msix_present(&proxy->pci_dev)) { qemu_get_be16s(f, &proxy->vdev->config_vector); @@ -246,6 +247,7 @@ void virtio_pci_reset(DeviceState *d) VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev); virtio_pci_stop_ioeventfd(proxy); virtio_reset(proxy->vdev); + msix_unuse_all_vectors(&proxy->pci_dev); proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } @@ -886,6 +888,7 @@ static Property virtio_blk_properties[] = { #ifdef __linux__ DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true), #endif + DEFINE_PROP_BIT("config-wce", VirtIOPCIProxy, blk.config_wce, 0, true), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index c4a5b22..c1b47a8 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -24,11 +24,6 @@ #define VIRTIO_SCSI_MAX_TARGET 255 #define VIRTIO_SCSI_MAX_LUN 16383 -/* Feature Bits */ -#define VIRTIO_SCSI_F_INOUT 0 -#define VIRTIO_SCSI_F_HOTPLUG 1 -#define VIRTIO_SCSI_F_CHANGE 2 - /* Response codes */ #define VIRTIO_SCSI_S_OK 0 #define VIRTIO_SCSI_S_OVERRUN 1 @@ -305,11 +300,17 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) goto incorrect_lun; } QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - if (r->tag == req->req.tmf->tag) { + VirtIOSCSIReq *cmd_req = r->hba_private; + if (cmd_req && cmd_req->req.cmd->tag == req->req.tmf->tag) { break; } } - if (r && r->hba_private) { + if (r) { + /* + * Assert that the request has not been completed yet, we + * check for it in the loop above. + */ + assert(r->hba_private); if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { /* "If the specified command is present in the task set, then * return a service response set to FUNCTION SUCCEEDED". @@ -555,8 +556,6 @@ static void virtio_scsi_set_config(VirtIODevice *vdev, static uint32_t virtio_scsi_get_features(VirtIODevice *vdev, uint32_t requested_features) { - requested_features |= (1UL << VIRTIO_SCSI_F_HOTPLUG); - requested_features |= (1UL << VIRTIO_SCSI_F_CHANGE); return requested_features; } diff --git a/hw/virtio-scsi.h b/hw/virtio-scsi.h index 4bc889d..91924f6 100644 --- a/hw/virtio-scsi.h +++ b/hw/virtio-scsi.h @@ -21,6 +21,11 @@ /* The ID for virtio_scsi */ #define VIRTIO_ID_SCSI 8 +/* Feature Bits */ +#define VIRTIO_SCSI_F_INOUT 0 +#define VIRTIO_SCSI_F_HOTPLUG 1 +#define VIRTIO_SCSI_F_CHANGE 2 + struct VirtIOSCSIConf { uint32_t num_queues; uint32_t max_sectors; @@ -31,6 +36,8 @@ struct VirtIOSCSIConf { DEFINE_VIRTIO_COMMON_FEATURES(_state, _features_field), \ DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \ DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF), \ - DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128) + DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128), \ + DEFINE_PROP_BIT("hotplug", _state, _features_field, VIRTIO_SCSI_F_HOTPLUG, true), \ + DEFINE_PROP_BIT("param_change", _state, _features_field, VIRTIO_SCSI_F_CHANGE, true) #endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/hw/xen_platform.c b/hw/xen_platform.c index c1fe984..0d6c2ff 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -87,9 +87,6 @@ static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) { if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET) { - /* Until qdev_free includes a call to object_unparent, we call it here - */ - object_unparent(&d->qdev.parent_obj); qdev_free(&d->qdev); } } @@ -315,18 +315,24 @@ static void ics_eoi(struct ics_state *ics, int nr) * Exported functions */ -qemu_irq xics_assign_irq(struct icp_state *icp, int irq, - enum xics_irq_type type) +qemu_irq xics_get_qirq(struct icp_state *icp, int irq) { if ((irq < icp->ics->offset) || (irq >= (icp->ics->offset + icp->ics->nr_irqs))) { return NULL; } + return icp->ics->qirqs[irq - icp->ics->offset]; +} + +void xics_set_irq_type(struct icp_state *icp, int irq, + enum xics_irq_type type) +{ + assert((irq >= icp->ics->offset) + && (irq < (icp->ics->offset + icp->ics->nr_irqs))); assert((type == XICS_MSI) || (type == XICS_LSI)); icp->ics->irqs[irq - icp->ics->offset].type = type; - return icp->ics->qirqs[irq - icp->ics->offset]; } static target_ulong h_cppr(CPUPPCState *env, sPAPREnvironment *spapr, @@ -36,8 +36,9 @@ enum xics_irq_type { XICS_LSI, /* Level-signalled interrupt */ }; -qemu_irq xics_assign_irq(struct icp_state *icp, int irq, - enum xics_irq_type type); +qemu_irq xics_get_qirq(struct icp_state *icp, int irq); +void xics_set_irq_type(struct icp_state *icp, int irq, + enum xics_irq_type type); struct icp_state *xics_system_init(int nr_irqs); diff --git a/hw/xilinx.h b/hw/xilinx.h index 7df21eb..556c5aa 100644 --- a/hw/xilinx.h +++ b/hw/xilinx.h @@ -1,3 +1,4 @@ +#include "stream.h" #include "qemu-common.h" #include "net.h" @@ -49,8 +50,8 @@ xilinx_ethlite_create(NICInfo *nd, target_phys_addr_t base, qemu_irq irq, } static inline DeviceState * -xilinx_axiethernet_create(void *dmach, - NICInfo *nd, target_phys_addr_t base, qemu_irq irq, +xilinx_axiethernet_create(NICInfo *nd, StreamSlave *peer, + target_phys_addr_t base, qemu_irq irq, int txmem, int rxmem) { DeviceState *dev; @@ -60,7 +61,7 @@ xilinx_axiethernet_create(void *dmach, qdev_set_nic_properties(dev, nd); qdev_prop_set_uint32(dev, "rxmem", rxmem); qdev_prop_set_uint32(dev, "txmem", txmem); - qdev_prop_set_ptr(dev, "dmach", dmach); + object_property_set_link(OBJECT(dev), OBJECT(peer), "tx_dev", NULL); qdev_init_nofail(dev); sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); @@ -68,21 +69,16 @@ xilinx_axiethernet_create(void *dmach, return dev; } -static inline DeviceState * -xilinx_axiethernetdma_create(void *dmach, - target_phys_addr_t base, qemu_irq irq, - qemu_irq irq2, int freqhz) +static inline void +xilinx_axiethernetdma_init(DeviceState *dev, StreamSlave *peer, + target_phys_addr_t base, qemu_irq irq, + qemu_irq irq2, int freqhz) { - DeviceState *dev = NULL; - - dev = qdev_create(NULL, "xlnx.axi-dma"); qdev_prop_set_uint32(dev, "freqhz", freqhz); - qdev_prop_set_ptr(dev, "dmach", dmach); + object_property_set_link(OBJECT(dev), OBJECT(peer), "tx_dev", NULL); qdev_init_nofail(dev); sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); sysbus_connect_irq(sysbus_from_qdev(dev), 1, irq2); - - return dev; } diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c index f4bec37..0e28c51 100644 --- a/hw/xilinx_axidma.c +++ b/hw/xilinx_axidma.c @@ -29,7 +29,7 @@ #include "qemu-log.h" #include "qdev-addr.h" -#include "xilinx_axidma.h" +#include "stream.h" #define D(x) @@ -77,7 +77,7 @@ enum { SDESC_STATUS_COMPLETE = (1 << 31) }; -struct AXIStream { +struct Stream { QEMUBH *bh; ptimer_state *ptimer; qemu_irq irq; @@ -94,9 +94,9 @@ struct XilinxAXIDMA { SysBusDevice busdev; MemoryRegion iomem; uint32_t freqhz; - void *dmach; + StreamSlave *tx_dev; - struct AXIStream streams[2]; + struct Stream streams[2]; }; /* @@ -113,27 +113,27 @@ static inline int stream_desc_eof(struct SDesc *d) return d->control & SDESC_CTRL_EOF; } -static inline int stream_resetting(struct AXIStream *s) +static inline int stream_resetting(struct Stream *s) { return !!(s->regs[R_DMACR] & DMACR_RESET); } -static inline int stream_running(struct AXIStream *s) +static inline int stream_running(struct Stream *s) { return s->regs[R_DMACR] & DMACR_RUNSTOP; } -static inline int stream_halted(struct AXIStream *s) +static inline int stream_halted(struct Stream *s) { return s->regs[R_DMASR] & DMASR_HALTED; } -static inline int stream_idle(struct AXIStream *s) +static inline int stream_idle(struct Stream *s) { return !!(s->regs[R_DMASR] & DMASR_IDLE); } -static void stream_reset(struct AXIStream *s) +static void stream_reset(struct Stream *s) { s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ @@ -159,7 +159,7 @@ static void stream_desc_show(struct SDesc *d) } #endif -static void stream_desc_load(struct AXIStream *s, target_phys_addr_t addr) +static void stream_desc_load(struct Stream *s, target_phys_addr_t addr) { struct SDesc *d = &s->desc; int i; @@ -176,7 +176,7 @@ static void stream_desc_load(struct AXIStream *s, target_phys_addr_t addr) } } -static void stream_desc_store(struct AXIStream *s, target_phys_addr_t addr) +static void stream_desc_store(struct Stream *s, target_phys_addr_t addr) { struct SDesc *d = &s->desc; int i; @@ -192,7 +192,7 @@ static void stream_desc_store(struct AXIStream *s, target_phys_addr_t addr) cpu_physical_memory_write(addr, (void *) d, sizeof *d); } -static void stream_update_irq(struct AXIStream *s) +static void stream_update_irq(struct Stream *s) { unsigned int pending, mask, irq; @@ -204,7 +204,7 @@ static void stream_update_irq(struct AXIStream *s) qemu_set_irq(s->irq, !!irq); } -static void stream_reload_complete_cnt(struct AXIStream *s) +static void stream_reload_complete_cnt(struct Stream *s) { unsigned int comp_th; comp_th = (s->regs[R_DMACR] >> 16) & 0xff; @@ -213,14 +213,14 @@ static void stream_reload_complete_cnt(struct AXIStream *s) static void timer_hit(void *opaque) { - struct AXIStream *s = opaque; + struct Stream *s = opaque; stream_reload_complete_cnt(s); s->regs[R_DMASR] |= DMASR_DLY_IRQ; stream_update_irq(s); } -static void stream_complete(struct AXIStream *s) +static void stream_complete(struct Stream *s) { unsigned int comp_delay; @@ -240,8 +240,8 @@ static void stream_complete(struct AXIStream *s) } } -static void stream_process_mem2s(struct AXIStream *s, - struct XilinxDMAConnection *dmach) +static void stream_process_mem2s(struct Stream *s, + StreamSlave *tx_dev) { uint32_t prev_d; unsigned char txbuf[16 * 1024]; @@ -276,7 +276,7 @@ static void stream_process_mem2s(struct AXIStream *s, s->pos += txlen; if (stream_desc_eof(&s->desc)) { - xlx_dma_push_to_client(dmach, txbuf, s->pos, app); + stream_push(tx_dev, txbuf, s->pos, app); s->pos = 0; stream_complete(s); } @@ -295,7 +295,7 @@ static void stream_process_mem2s(struct AXIStream *s, } } -static void stream_process_s2mem(struct AXIStream *s, +static void stream_process_s2mem(struct Stream *s, unsigned char *buf, size_t len, uint32_t *app) { uint32_t prev_d; @@ -351,11 +351,11 @@ static void stream_process_s2mem(struct AXIStream *s, } } -static -void axidma_push(void *opaque, unsigned char *buf, size_t len, uint32_t *app) +static void +axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app) { - struct XilinxAXIDMA *d = opaque; - struct AXIStream *s = &d->streams[1]; + struct XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj)); + struct Stream *s = &d->streams[1]; if (!app) { hw_error("No stream app data!\n"); @@ -368,7 +368,7 @@ static uint64_t axidma_read(void *opaque, target_phys_addr_t addr, unsigned size) { struct XilinxAXIDMA *d = opaque; - struct AXIStream *s; + struct Stream *s; uint32_t r = 0; int sid; @@ -403,7 +403,7 @@ static void axidma_write(void *opaque, target_phys_addr_t addr, uint64_t value, unsigned size) { struct XilinxAXIDMA *d = opaque; - struct AXIStream *s; + struct Stream *s; int sid; sid = streamid_from_addr(addr); @@ -440,7 +440,7 @@ static void axidma_write(void *opaque, target_phys_addr_t addr, s->regs[addr] = value; s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */ if (!sid) { - stream_process_mem2s(s, d->dmach); + stream_process_mem2s(s, d->tx_dev); } break; default: @@ -466,12 +466,6 @@ static int xilinx_axidma_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->streams[0].irq); sysbus_init_irq(dev, &s->streams[1].irq); - if (!s->dmach) { - hw_error("Unconnected DMA channel.\n"); - } - - xlx_dma_connect_dma(s->dmach, s, axidma_push); - memory_region_init_io(&s->iomem, &axidma_ops, s, "xlnx.axi-dma", R_MAX * 4 * 2); sysbus_init_mmio(dev, &s->iomem); @@ -486,9 +480,16 @@ static int xilinx_axidma_init(SysBusDevice *dev) return 0; } +static void xilinx_axidma_initfn(Object *obj) +{ + struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + + object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, + (Object **) &s->tx_dev, NULL); +} + static Property axidma_properties[] = { DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000), - DEFINE_PROP_PTR("dmach", struct XilinxAXIDMA, dmach), DEFINE_PROP_END_OF_LIST(), }; @@ -496,9 +497,11 @@ static void axidma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); k->init = xilinx_axidma_init; dc->props = axidma_properties; + ssc->push = axidma_push; } static TypeInfo axidma_info = { @@ -506,6 +509,11 @@ static TypeInfo axidma_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct XilinxAXIDMA), .class_init = axidma_class_init, + .instance_init = xilinx_axidma_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SLAVE }, + { } + } }; static void xilinx_axidma_register_types(void) diff --git a/hw/xilinx_axidma.h b/hw/xilinx_axidma.h deleted file mode 100644 index 37cb6f0..0000000 --- a/hw/xilinx_axidma.h +++ /dev/null @@ -1,39 +0,0 @@ -/* AXI DMA connection. Used until qdev provides a generic way. */ -typedef void (*DMAPushFn)(void *opaque, - unsigned char *buf, size_t len, uint32_t *app); - -struct XilinxDMAConnection { - void *dma; - void *client; - - DMAPushFn to_dma; - DMAPushFn to_client; -}; - -static inline void xlx_dma_connect_client(struct XilinxDMAConnection *dmach, - void *c, DMAPushFn f) -{ - dmach->client = c; - dmach->to_client = f; -} - -static inline void xlx_dma_connect_dma(struct XilinxDMAConnection *dmach, - void *d, DMAPushFn f) -{ - dmach->dma = d; - dmach->to_dma = f; -} - -static inline -void xlx_dma_push_to_dma(struct XilinxDMAConnection *dmach, - uint8_t *buf, size_t len, uint32_t *app) -{ - dmach->to_dma(dmach->dma, buf, len, app); -} -static inline -void xlx_dma_push_to_client(struct XilinxDMAConnection *dmach, - uint8_t *buf, size_t len, uint32_t *app) -{ - dmach->to_client(dmach->client, buf, len, app); -} - diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c index 9b08c62..eec155d 100644 --- a/hw/xilinx_axienet.c +++ b/hw/xilinx_axienet.c @@ -28,7 +28,7 @@ #include "net.h" #include "net/checksum.h" -#include "xilinx_axidma.h" +#include "stream.h" #define DPHY(x) @@ -310,7 +310,7 @@ struct XilinxAXIEnet { SysBusDevice busdev; MemoryRegion iomem; qemu_irq irq; - void *dmach; + StreamSlave *tx_dev; NICState *nic; NICConf conf; @@ -772,7 +772,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) /* Good frame. */ app[2] |= 1 << 6; - xlx_dma_push_to_dma(s->dmach, (void *)s->rxmem, size, app); + stream_push(s->tx_dev, (void *)s->rxmem, size, app); s->regs[R_IS] |= IS_RX_COMPLETE; enet_update_irq(s); @@ -788,9 +788,9 @@ static void eth_cleanup(NetClientState *nc) } static void -axienet_stream_push(void *opaque, uint8_t *buf, size_t size, uint32_t *hdr) +axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr) { - struct XilinxAXIEnet *s = opaque; + struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); /* TX enable ? */ if (!(s->tc & TC_TX)) { @@ -844,12 +844,6 @@ static int xilinx_enet_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq); - if (!s->dmach) { - hw_error("Unconnected Xilinx Ethernet MAC.\n"); - } - - xlx_dma_connect_client(s->dmach, s, axienet_stream_push); - memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000); sysbus_init_mmio(dev, &s->iomem); @@ -869,11 +863,18 @@ static int xilinx_enet_init(SysBusDevice *dev) return 0; } +static void xilinx_enet_initfn(Object *obj) +{ + struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + + object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, + (Object **) &s->tx_dev, NULL); +} + static Property xilinx_enet_properties[] = { DEFINE_PROP_UINT32("phyaddr", struct XilinxAXIEnet, c_phyaddr, 7), DEFINE_PROP_UINT32("rxmem", struct XilinxAXIEnet, c_rxmem, 0x1000), DEFINE_PROP_UINT32("txmem", struct XilinxAXIEnet, c_txmem, 0x1000), - DEFINE_PROP_PTR("dmach", struct XilinxAXIEnet, dmach), DEFINE_NIC_PROPERTIES(struct XilinxAXIEnet, conf), DEFINE_PROP_END_OF_LIST(), }; @@ -882,9 +883,11 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); k->init = xilinx_enet_init; dc->props = xilinx_enet_properties; + ssc->push = axienet_stream_push; } static TypeInfo xilinx_enet_info = { @@ -892,6 +895,11 @@ static TypeInfo xilinx_enet_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct XilinxAXIEnet), .class_init = xilinx_enet_class_init, + .instance_init = xilinx_enet_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SLAVE }, + { } + } }; static void xilinx_enet_register_types(void) |