diff options
Diffstat (limited to 'hw')
| -rw-r--r-- | hw/9pfs/virtio-9p.c | 8 | ||||
| -rw-r--r-- | hw/alpha_dp264.c | 2 | ||||
| -rw-r--r-- | hw/alpha_sys.h | 3 | ||||
| -rw-r--r-- | hw/alpha_typhoon.c | 14 | ||||
| -rw-r--r-- | hw/an5206.c | 2 | ||||
| -rw-r--r-- | hw/apb_pci.c | 49 | ||||
| -rw-r--r-- | hw/apb_pci.h | 3 | ||||
| -rw-r--r-- | hw/apic.c | 126 | ||||
| -rw-r--r-- | hw/apic.h | 4 | ||||
| -rw-r--r-- | hw/apic_common.c | 69 | ||||
| -rw-r--r-- | hw/apic_internal.h | 27 | ||||
| -rw-r--r-- | hw/arm-misc.h | 9 | ||||
| -rw-r--r-- | hw/arm11mpcore.c | 22 | ||||
| -rw-r--r-- | hw/arm_boot.c | 112 | ||||
| -rw-r--r-- | hw/arm_gic.c | 58 | ||||
| -rw-r--r-- | hw/arm_mptimer.c | 2 | ||||
| -rw-r--r-- | hw/arm_pic.c | 4 | ||||
| -rw-r--r-- | hw/armv7m.c | 4 | ||||
| -rw-r--r-- | hw/axis_dev88.c | 2 | ||||
| -rw-r--r-- | hw/cadence_gem.c | 1233 | ||||
| -rw-r--r-- | hw/cadence_ttc.c | 489 | ||||
| -rw-r--r-- | hw/cadence_uart.c | 513 | ||||
| -rw-r--r-- | hw/cris-boot.c | 6 | ||||
| -rw-r--r-- | hw/cris-boot.h | 2 | ||||
| -rw-r--r-- | hw/cris_pic_cpu.c | 4 | ||||
| -rw-r--r-- | hw/cuda.c | 2 | ||||
| -rw-r--r-- | hw/ds1338.c | 1 | ||||
| -rw-r--r-- | hw/dummy_m68k.c | 2 | ||||
| -rw-r--r-- | hw/etraxfs.h | 2 | ||||
| -rw-r--r-- | hw/etraxfs_ser.c | 4 | ||||
| -rw-r--r-- | hw/exynos4210.h | 2 | ||||
| -rw-r--r-- | hw/exynos4210_mct.c | 2 | ||||
| -rw-r--r-- | hw/fdc.c | 142 | ||||
| -rw-r--r-- | hw/grlib.h | 2 | ||||
| -rw-r--r-- | hw/highbank.c | 8 | ||||
| -rw-r--r-- | hw/i8254.c | 281 | ||||
| -rw-r--r-- | hw/i8254.h | 11 | ||||
| -rw-r--r-- | hw/i8254_common.c | 311 | ||||
| -rw-r--r-- | hw/i8254_internal.h | 85 | ||||
| -rw-r--r-- | hw/ide/ahci.c | 4 | ||||
| -rw-r--r-- | hw/ide/cmd646.c | 6 | ||||
| -rw-r--r-- | hw/ide/core.c | 24 | ||||
| -rw-r--r-- | hw/ide/macio.c | 2 | ||||
| -rw-r--r-- | hw/ide/piix.c | 2 | ||||
| -rw-r--r-- | hw/ide/via.c | 2 | ||||
| -rw-r--r-- | hw/integratorcp.c | 2 | ||||
| -rw-r--r-- | hw/ioapic.c | 2 | ||||
| -rw-r--r-- | hw/kvm/apic.c | 34 | ||||
| -rw-r--r-- | hw/kvm/clock.c | 2 | ||||
| -rw-r--r-- | hw/kvm/i8254.c | 254 | ||||
| -rw-r--r-- | hw/kvmvapic.c | 807 | ||||
| -rw-r--r-- | hw/leon3.c | 10 | ||||
| -rw-r--r-- | hw/lm32_boards.c | 12 | ||||
| -rw-r--r-- | hw/mc146818rtc.c | 7 | ||||
| -rw-r--r-- | hw/mcf.h | 4 | ||||
| -rw-r--r-- | hw/mcf5206.c | 4 | ||||
| -rw-r--r-- | hw/mcf5208.c | 2 | ||||
| -rw-r--r-- | hw/mcf_intc.c | 4 | ||||
| -rw-r--r-- | hw/microblaze_boot.c | 177 | ||||
| -rw-r--r-- | hw/microblaze_boot.h | 10 | ||||
| -rw-r--r-- | hw/microblaze_pic_cpu.c | 4 | ||||
| -rw-r--r-- | hw/microblaze_pic_cpu.h | 2 | ||||
| -rw-r--r-- | hw/milkymist.c | 10 | ||||
| -rw-r--r-- | hw/mips_cpudevs.h | 4 | ||||
| -rw-r--r-- | hw/mips_fulong2e.c | 19 | ||||
| -rw-r--r-- | hw/mips_int.c | 6 | ||||
| -rw-r--r-- | hw/mips_jazz.c | 8 | ||||
| -rw-r--r-- | hw/mips_malta.c | 99 | ||||
| -rw-r--r-- | hw/mips_mipssim.c | 8 | ||||
| -rw-r--r-- | hw/mips_r4k.c | 8 | ||||
| -rw-r--r-- | hw/mips_timer.c | 20 | ||||
| -rw-r--r-- | hw/mipsnet.c | 2 | ||||
| -rw-r--r-- | hw/mpc8544_guts.c | 2 | ||||
| -rw-r--r-- | hw/musicpal.c | 2 | ||||
| -rw-r--r-- | hw/nseries.c | 12 | ||||
| -rw-r--r-- | hw/omap.h | 15 | ||||
| -rw-r--r-- | hw/omap1.c | 15 | ||||
| -rw-r--r-- | hw/omap2.c | 37 | ||||
| -rw-r--r-- | hw/omap_i2c.c | 107 | ||||
| -rw-r--r-- | hw/opencores_eth.c | 4 | ||||
| -rw-r--r-- | hw/openpic.c | 12 | ||||
| -rw-r--r-- | hw/pc.c | 39 | ||||
| -rw-r--r-- | hw/pc_piix.c | 32 | ||||
| -rw-r--r-- | hw/pci.c | 1 | ||||
| -rw-r--r-- | hw/petalogix_ml605_mmu.c | 146 | ||||
| -rw-r--r-- | hw/petalogix_s3adsp1800_mmu.c | 149 | ||||
| -rw-r--r-- | hw/ppc.c | 123 | ||||
| -rw-r--r-- | hw/ppc.h | 28 | ||||
| -rw-r--r-- | hw/ppc405.h | 8 | ||||
| -rw-r--r-- | hw/ppc405_uc.c | 52 | ||||
| -rw-r--r-- | hw/ppc440_bamboo.c | 8 | ||||
| -rw-r--r-- | hw/ppc4xx.h | 8 | ||||
| -rw-r--r-- | hw/ppc4xx_devs.c | 17 | ||||
| -rw-r--r-- | hw/ppc_booke.c | 20 | ||||
| -rw-r--r-- | hw/ppc_newworld.c | 14 | ||||
| -rw-r--r-- | hw/ppc_oldworld.c | 14 | ||||
| -rw-r--r-- | hw/ppc_prep.c | 16 | ||||
| -rw-r--r-- | hw/ppce500_mpc8544ds.c | 16 | ||||
| -rw-r--r-- | hw/ppce500_spin.c | 10 | ||||
| -rw-r--r-- | hw/pxa.h | 6 | ||||
| -rw-r--r-- | hw/pxa2xx.c | 5 | ||||
| -rw-r--r-- | hw/pxa2xx_dma.c | 12 | ||||
| -rw-r--r-- | hw/pxa2xx_gpio.c | 4 | ||||
| -rw-r--r-- | hw/pxa2xx_lcd.c | 12 | ||||
| -rw-r--r-- | hw/pxa2xx_pic.c | 4 | ||||
| -rw-r--r-- | hw/qdev-properties.c | 4 | ||||
| -rw-r--r-- | hw/qxl-render.c | 167 | ||||
| -rw-r--r-- | hw/qxl.c | 367 | ||||
| -rw-r--r-- | hw/qxl.h | 31 | ||||
| -rw-r--r-- | hw/r2d.c | 8 | ||||
| -rw-r--r-- | hw/realview.c | 5 | ||||
| -rw-r--r-- | hw/s390-virtio-bus.c | 6 | ||||
| -rw-r--r-- | hw/s390-virtio.c | 46 | ||||
| -rw-r--r-- | hw/scsi-bus.c | 18 | ||||
| -rw-r--r-- | hw/scsi-disk.c | 49 | ||||
| -rw-r--r-- | hw/sh.h | 2 | ||||
| -rw-r--r-- | hw/sh7750.c | 2 | ||||
| -rw-r--r-- | hw/sh_intc.c | 2 | ||||
| -rw-r--r-- | hw/shix.c | 2 | ||||
| -rw-r--r-- | hw/spapr.c | 22 | ||||
| -rw-r--r-- | hw/spapr.h | 17 | ||||
| -rw-r--r-- | hw/spapr_hcall.c | 42 | ||||
| -rw-r--r-- | hw/spapr_llan.c | 10 | ||||
| -rw-r--r-- | hw/spapr_pci.c | 195 | ||||
| -rw-r--r-- | hw/spapr_pci.h | 4 | ||||
| -rw-r--r-- | hw/spapr_rtas.c | 4 | ||||
| -rw-r--r-- | hw/spapr_vio.c | 14 | ||||
| -rw-r--r-- | hw/spapr_vty.c | 4 | ||||
| -rw-r--r-- | hw/strongarm.h | 2 | ||||
| -rw-r--r-- | hw/sun4m.c | 16 | ||||
| -rw-r--r-- | hw/sun4u.c | 83 | ||||
| -rw-r--r-- | hw/usb-ohci.h | 9 | ||||
| -rw-r--r-- | hw/usb-uhci.h | 10 | ||||
| -rw-r--r-- | hw/usb.h | 22 | ||||
| -rw-r--r-- | hw/usb/bus.c (renamed from hw/usb-bus.c) | 24 | ||||
| -rw-r--r-- | hw/usb/core.c (renamed from hw/usb.c) | 133 | ||||
| -rw-r--r-- | hw/usb/desc.c (renamed from hw/usb-desc.c) | 24 | ||||
| -rw-r--r-- | hw/usb/desc.h (renamed from hw/usb-desc.h) | 0 | ||||
| -rw-r--r-- | hw/usb/dev-audio.c (renamed from hw/usb-audio.c) | 8 | ||||
| -rw-r--r-- | hw/usb/dev-bluetooth.c (renamed from hw/usb-bt.c) | 10 | ||||
| -rw-r--r-- | hw/usb/dev-hid.c (renamed from hw/usb-hid.c) | 11 | ||||
| -rw-r--r-- | hw/usb/dev-hub.c (renamed from hw/usb-hub.c) | 4 | ||||
| -rw-r--r-- | hw/usb/dev-network.c (renamed from hw/usb-net.c) | 8 | ||||
| -rw-r--r-- | hw/usb/dev-serial.c (renamed from hw/usb-serial.c) | 12 | ||||
| -rw-r--r-- | hw/usb/dev-smartcard-reader.c (renamed from hw/usb-ccid.c) | 8 | ||||
| -rw-r--r-- | hw/usb/dev-storage.c (renamed from hw/usb-msd.c) | 16 | ||||
| -rw-r--r-- | hw/usb/dev-wacom.c (renamed from hw/usb-wacom.c) | 6 | ||||
| -rw-r--r-- | hw/usb/hcd-ehci.c (renamed from hw/usb-ehci.c) | 263 | ||||
| -rw-r--r-- | hw/usb/hcd-musb.c (renamed from hw/usb-musb.c) | 6 | ||||
| -rw-r--r-- | hw/usb/hcd-ohci.c (renamed from hw/usb-ohci.c) | 35 | ||||
| -rw-r--r-- | hw/usb/hcd-uhci.c (renamed from hw/usb-uhci.c) | 438 | ||||
| -rw-r--r-- | hw/usb/hcd-xhci.c (renamed from hw/usb-xhci.c) | 31 | ||||
| -rw-r--r-- | hw/usb/host-bsd.c | 647 | ||||
| -rw-r--r-- | hw/usb/host-linux.c | 1913 | ||||
| -rw-r--r-- | hw/usb/host-stub.c | 52 | ||||
| -rw-r--r-- | hw/usb/libhw.c (renamed from hw/usb-libhw.c) | 2 | ||||
| -rw-r--r-- | hw/usb/redirect.c | 1485 | ||||
| -rw-r--r-- | hw/versatilepb.c | 5 | ||||
| -rw-r--r-- | hw/vexpress.c | 4 | ||||
| -rw-r--r-- | hw/vga.c | 2 | ||||
| -rw-r--r-- | hw/vhost.c | 33 | ||||
| -rw-r--r-- | hw/virtex_ml507.c | 12 | ||||
| -rw-r--r-- | hw/virtio-scsi.c | 2 | ||||
| -rw-r--r-- | hw/vmport.c | 12 | ||||
| -rw-r--r-- | hw/xen_machine_pv.c | 2 | ||||
| -rw-r--r-- | hw/xics.c | 135 | ||||
| -rw-r--r-- | hw/xics.h | 8 | ||||
| -rw-r--r-- | hw/xilinx_zynq.c | 157 | ||||
| -rw-r--r-- | hw/xtensa_lx60.c | 10 | ||||
| -rw-r--r-- | hw/xtensa_pic.c | 16 | ||||
| -rw-r--r-- | hw/xtensa_sim.c | 4 | ||||
| -rw-r--r-- | hw/zynq_slcr.c | 535 |
172 files changed, 11246 insertions, 2123 deletions
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index a72ffc3390..c633fb9b7e 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -1349,7 +1349,9 @@ static void v9fs_open(void *opaque) if (s->proto_version == V9FS_PROTO_2000L) { err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode); } else { - err = pdu_unmarshal(pdu, offset, "db", &fid, &mode); + uint8_t modebyte; + err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte); + mode = modebyte; } if (err < 0) { goto out_nofid; @@ -3260,9 +3262,9 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) ptr = pdu->elem.out_sg[0].iov_base; - memcpy(&pdu->size, ptr, 4); + pdu->size = le32_to_cpu(*(uint32_t *)ptr); pdu->id = ptr[4]; - memcpy(&pdu->tag, ptr + 5, 2); + pdu->tag = le16_to_cpu(*(uint16_t *)(ptr + 5)); qemu_co_queue_init(&pdu->complete); submit_pdu(s, pdu); } diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c index ea0fd95f84..9eb939f383 100644 --- a/hw/alpha_dp264.c +++ b/hw/alpha_dp264.c @@ -49,7 +49,7 @@ static void clipper_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - CPUState *cpus[4]; + CPUAlphaState *cpus[4]; PCIBus *pci_bus; ISABus *isa_bus; qemu_irq rtc_irq; diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h index d54b18f8ed..de40f8b613 100644 --- a/hw/alpha_sys.h +++ b/hw/alpha_sys.h @@ -8,11 +8,10 @@ #include "ide.h" #include "net.h" #include "pc.h" -#include "usb-ohci.h" #include "irq.h" -PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, CPUState *[4], +PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, CPUAlphaState *[4], pci_map_irq_fn); /* alpha_pci.c. */ diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c index b539416589..872e1122e8 100644 --- a/hw/alpha_typhoon.c +++ b/hw/alpha_typhoon.c @@ -21,7 +21,7 @@ typedef struct TyphoonCchip { uint64_t drir; uint64_t dim[4]; uint32_t iic[4]; - CPUState *cpu[4]; + CPUAlphaState *cpu[4]; } TyphoonCchip; typedef struct TyphoonWindow { @@ -52,7 +52,7 @@ typedef struct TyphoonState { } TyphoonState; /* Called when one of DRIR or DIM changes. */ -static void cpu_irq_change(CPUState *env, uint64_t req) +static void cpu_irq_change(CPUAlphaState *env, uint64_t req) { /* If there are any non-masked interrupts, tell the cpu. */ if (env) { @@ -66,7 +66,7 @@ static void cpu_irq_change(CPUState *env, uint64_t req) static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size) { - CPUState *env = cpu_single_env; + CPUAlphaState *env = cpu_single_env; TyphoonState *s = opaque; uint64_t ret = 0; @@ -347,7 +347,7 @@ static void cchip_write(void *opaque, target_phys_addr_t addr, if ((newval ^ oldval) & 0xff0) { int i; for (i = 0; i < 4; ++i) { - CPUState *env = s->cchip.cpu[i]; + CPUAlphaState *env = s->cchip.cpu[i]; if (env) { /* IPI can be either cleared or set by the write. */ if (newval & (1 << (i + 8))) { @@ -655,7 +655,7 @@ static void typhoon_set_timer_irq(void *opaque, int irq, int level) /* Deliver the interrupt to each CPU, considering each CPU's IIC. */ for (i = 0; i < 4; ++i) { - CPUState *env = s->cchip.cpu[i]; + CPUAlphaState *env = s->cchip.cpu[i]; if (env) { uint32_t iic = s->cchip.iic[i]; @@ -693,7 +693,7 @@ static void typhoon_alarm_timer(void *opaque) PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, qemu_irq *p_rtc_irq, - CPUState *cpus[4], pci_map_irq_fn sys_map_irq) + CPUAlphaState *cpus[4], pci_map_irq_fn sys_map_irq) { const uint64_t MB = 1024 * 1024; const uint64_t GB = 1024 * MB; @@ -713,7 +713,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Remember the CPUs so that we can deliver interrupts to them. */ for (i = 0; i < 4; i++) { - CPUState *env = cpus[i]; + CPUAlphaState *env = cpus[i]; s->cchip.cpu[i] = env; if (env) { env->alarm_timer = qemu_new_timer_ns(rtc_clock, diff --git a/hw/an5206.c b/hw/an5206.c index d57306d3ad..25407c0f50 100644 --- a/hw/an5206.c +++ b/hw/an5206.c @@ -24,7 +24,7 @@ static void an5206_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUM68KState *env; int kernel_size; uint64_t elf_entry; target_phys_addr_t entry; diff --git a/hw/apb_pci.c b/hw/apb_pci.c index 1d25da8da9..7e28808ec4 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -66,6 +66,8 @@ do { printf("APB: " fmt , ## __VA_ARGS__); } while (0) #define RESET_WCMASK 0x98000000 #define RESET_WMASK 0x60000000 +#define MAX_IVEC 0x30 + typedef struct APBState { SysBusDevice busdev; PCIBus *bus; @@ -77,7 +79,8 @@ typedef struct APBState { uint32_t pci_control[16]; uint32_t pci_irq_map[8]; uint32_t obio_irq_map[32]; - qemu_irq pci_irqs[32]; + qemu_irq *pbm_irqs; + qemu_irq *ivec_irqs; uint32_t reset_control; unsigned int nr_resets; } APBState; @@ -87,7 +90,7 @@ static void apb_config_writel (void *opaque, target_phys_addr_t addr, { APBState *s = opaque; - APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val); + APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val); switch (addr & 0xffff) { case 0x30 ... 0x4f: /* DMA error registers */ @@ -104,6 +107,12 @@ static void apb_config_writel (void *opaque, target_phys_addr_t addr, s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK; } break; + case 0x1000 ... 0x1080: /* OBIO interrupt control */ + if (addr & 4) { + s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK; + s->obio_irq_map[(addr & 0xff) >> 3] |= val & ~PBM_PCI_IMR_MASK; + } + break; case 0x2000 ... 0x202f: /* PCI control */ s->pci_control[(addr & 0x3f) >> 2] = val; break; @@ -154,6 +163,13 @@ static uint64_t apb_config_readl (void *opaque, val = 0; } break; + case 0x1000 ... 0x1080: /* OBIO interrupt control */ + if (addr & 4) { + val = s->obio_irq_map[(addr & 0xff) >> 3]; + } else { + val = 0; + } + break; case 0x2000 ... 0x202f: /* PCI control */ val = s->pci_control[(addr & 0x3f) >> 2]; break; @@ -190,7 +206,7 @@ static void apb_pci_config_write(void *opaque, target_phys_addr_t addr, APBState *s = opaque; val = qemu_bswap_len(val, size); - APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val); + APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val); pci_data_write(s->bus, addr, val, size); } @@ -280,10 +296,19 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level) if (irq_num < 32) { if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) { APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); - qemu_set_irq(s->pci_irqs[irq_num], level); + qemu_set_irq(s->ivec_irqs[irq_num], level); } else { APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num); - qemu_irq_lower(s->pci_irqs[irq_num]); + qemu_irq_lower(s->ivec_irqs[irq_num]); + } + } else { + /* OBIO IRQ map onto the next 16 INO. */ + if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) { + APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); + qemu_set_irq(s->ivec_irqs[irq_num], level); + } else { + APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num); + qemu_irq_lower(s->ivec_irqs[irq_num]); } } } @@ -316,12 +341,12 @@ static int apb_pci_bridge_initfn(PCIDevice *dev) PCIBus *pci_apb_init(target_phys_addr_t special_base, target_phys_addr_t mem_base, - qemu_irq *pic, PCIBus **bus2, PCIBus **bus3) + qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3, + qemu_irq **pbm_irqs) { DeviceState *dev; SysBusDevice *s; APBState *d; - unsigned int i; PCIDevice *pci_dev; PCIBridge *br; @@ -346,9 +371,8 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, get_system_io(), 0, 32); - for (i = 0; i < 32; i++) { - sysbus_connect_irq(s, i, pic[i]); - } + *pbm_irqs = d->pbm_irqs; + d->ivec_irqs = ivec_irqs; pci_create_simple(d->bus, 0, "pbm-pci"); @@ -402,9 +426,7 @@ static int pci_pbm_init_device(SysBusDevice *dev) for (i = 0; i < 8; i++) { s->pci_irq_map[i] = (0x1f << 6) | (i << 2); } - for (i = 0; i < 32; i++) { - sysbus_init_irq(dev, &s->pci_irqs[i]); - } + s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC); /* apb_config */ memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config", @@ -444,7 +466,6 @@ static void pbm_pci_host_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_SUN; k->device_id = PCI_DEVICE_ID_SUN_SABRE; k->class_id = PCI_CLASS_BRIDGE_HOST; - k->is_bridge = 1; } static TypeInfo pbm_pci_host_info = { diff --git a/hw/apb_pci.h b/hw/apb_pci.h index 8869f9d326..55f7c4c3b2 100644 --- a/hw/apb_pci.h +++ b/hw/apb_pci.h @@ -5,5 +5,6 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, target_phys_addr_t mem_base, - qemu_irq *pic, PCIBus **bus2, PCIBus **bus3); + qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3, + qemu_irq **pbm_irqs); #endif diff --git a/hw/apic.c b/hw/apic.c index ff9d24e914..4eeaf8801c 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -35,6 +35,10 @@ #define MSI_ADDR_DEST_ID_SHIFT 12 #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 +#define SYNC_FROM_VAPIC 0x1 +#define SYNC_TO_VAPIC 0x2 +#define SYNC_ISR_IRR_TO_VAPIC 0x4 + static APICCommonState *local_apics[MAX_APICS + 1]; static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode); @@ -78,6 +82,70 @@ static inline int get_bit(uint32_t *tab, int index) return !!(tab[i] & mask); } +/* return -1 if no bit is set */ +static int get_highest_priority_int(uint32_t *tab) +{ + int i; + for (i = 7; i >= 0; i--) { + if (tab[i] != 0) { + return i * 32 + fls_bit(tab[i]); + } + } + return -1; +} + +static void apic_sync_vapic(APICCommonState *s, int sync_type) +{ + VAPICState vapic_state; + size_t length; + off_t start; + int vector; + + if (!s->vapic_paddr) { + return; + } + if (sync_type & SYNC_FROM_VAPIC) { + cpu_physical_memory_rw(s->vapic_paddr, (void *)&vapic_state, + sizeof(vapic_state), 0); + s->tpr = vapic_state.tpr; + } + if (sync_type & (SYNC_TO_VAPIC | SYNC_ISR_IRR_TO_VAPIC)) { + start = offsetof(VAPICState, isr); + length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr); + + if (sync_type & SYNC_TO_VAPIC) { + assert(qemu_cpu_is_self(s->cpu_env)); + + vapic_state.tpr = s->tpr; + vapic_state.enabled = 1; + start = 0; + length = sizeof(VAPICState); + } + + vector = get_highest_priority_int(s->isr); + if (vector < 0) { + vector = 0; + } + vapic_state.isr = vector & 0xf0; + + vapic_state.zero = 0; + + vector = get_highest_priority_int(s->irr); + if (vector < 0) { + vector = 0; + } + vapic_state.irr = vector & 0xff; + + cpu_physical_memory_write_rom(s->vapic_paddr + start, + ((void *)&vapic_state) + start, length); + } +} + +static void apic_vapic_base_update(APICCommonState *s) +{ + apic_sync_vapic(s, SYNC_TO_VAPIC); +} + static void apic_local_deliver(APICCommonState *s, int vector) { uint32_t lvt = s->lvt[vector]; @@ -239,20 +307,17 @@ static void apic_set_base(APICCommonState *s, uint64_t val) static void apic_set_tpr(APICCommonState *s, uint8_t val) { - s->tpr = (val & 0x0f) << 4; - apic_update_irq(s); + /* Updates from cr8 are ignored while the VAPIC is active */ + if (!s->vapic_paddr) { + s->tpr = val << 4; + apic_update_irq(s); + } } -/* return -1 if no bit is set */ -static int get_highest_priority_int(uint32_t *tab) +static uint8_t apic_get_tpr(APICCommonState *s) { - int i; - for(i = 7; i >= 0; i--) { - if (tab[i] != 0) { - return i * 32 + fls_bit(tab[i]); - } - } - return -1; + apic_sync_vapic(s, SYNC_FROM_VAPIC); + return s->tpr >> 4; } static int apic_get_ppr(APICCommonState *s) @@ -312,6 +377,14 @@ static void apic_update_irq(APICCommonState *s) } } +void apic_poll_irq(DeviceState *d) +{ + APICCommonState *s = APIC_COMMON(d); + + apic_sync_vapic(s, SYNC_FROM_VAPIC); + apic_update_irq(s); +} + static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode) { apic_report_irq_delivered(!get_bit(s->irr, vector_num)); @@ -321,6 +394,16 @@ static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode) set_bit(s->tmr, vector_num); else reset_bit(s->tmr, vector_num); + if (s->vapic_paddr) { + apic_sync_vapic(s, SYNC_ISR_IRR_TO_VAPIC); + /* + * The vcpu thread needs to see the new IRR before we pull its current + * TPR value. That way, if we miss a lowering of the TRP, the guest + * has the chance to notice the new IRR and poll for IRQs on its own. + */ + smp_wmb(); + apic_sync_vapic(s, SYNC_FROM_VAPIC); + } apic_update_irq(s); } @@ -334,6 +417,7 @@ static void apic_eoi(APICCommonState *s) if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) { ioapic_eoi_broadcast(isrv); } + apic_sync_vapic(s, SYNC_FROM_VAPIC | SYNC_TO_VAPIC); apic_update_irq(s); } @@ -471,15 +555,19 @@ int apic_get_interrupt(DeviceState *d) if (!(s->spurious_vec & APIC_SV_ENABLE)) return -1; + apic_sync_vapic(s, SYNC_FROM_VAPIC); intno = apic_irq_pending(s); if (intno == 0) { + apic_sync_vapic(s, SYNC_TO_VAPIC); return -1; } else if (intno < 0) { + apic_sync_vapic(s, SYNC_TO_VAPIC); return s->spurious_vec & 0xff; } reset_bit(s->irr, intno); set_bit(s->isr, intno); + apic_sync_vapic(s, SYNC_TO_VAPIC); apic_update_irq(s); return intno; } @@ -576,6 +664,10 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */ break; case 0x08: + apic_sync_vapic(s, SYNC_FROM_VAPIC); + if (apic_report_tpr_access) { + cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_READ); + } val = s->tpr; break; case 0x09: @@ -675,7 +767,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) case 0x03: break; case 0x08: + if (apic_report_tpr_access) { + cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_WRITE); + } s->tpr = val; + apic_sync_vapic(s, SYNC_TO_VAPIC); apic_update_irq(s); break; case 0x09: @@ -737,6 +833,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } +static void apic_pre_save(APICCommonState *s) +{ + apic_sync_vapic(s, SYNC_FROM_VAPIC); +} + static void apic_post_load(APICCommonState *s) { if (s->timer_expiry != -1) { @@ -770,7 +871,10 @@ static void apic_class_init(ObjectClass *klass, void *data) k->init = apic_init; k->set_base = apic_set_base; k->set_tpr = apic_set_tpr; + k->get_tpr = apic_get_tpr; + k->vapic_base_update = apic_vapic_base_update; k->external_nmi = apic_external_nmi; + k->pre_save = apic_pre_save; k->post_load = apic_post_load; } diff --git a/hw/apic.h b/hw/apic.h index a62d83ba9f..62179cebee 100644 --- a/hw/apic.h +++ b/hw/apic.h @@ -18,9 +18,11 @@ void cpu_set_apic_tpr(DeviceState *s, uint8_t val); uint8_t cpu_get_apic_tpr(DeviceState *s); void apic_init_reset(DeviceState *s); void apic_sipi(DeviceState *s); +void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, + TPRAccess access); /* pc.c */ -int cpu_is_bsp(CPUState *env); +int cpu_is_bsp(CPUX86State *env); DeviceState *cpu_get_current_apic(void); #endif diff --git a/hw/apic_common.c b/hw/apic_common.c index c91f7d5391..60b82596e7 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -20,8 +20,10 @@ #include "apic.h" #include "apic_internal.h" #include "trace.h" +#include "kvm.h" static int apic_irq_delivered; +bool apic_report_tpr_access; void cpu_set_apic_base(DeviceState *d, uint64_t val) { @@ -63,9 +65,45 @@ void cpu_set_apic_tpr(DeviceState *d, uint8_t val) uint8_t cpu_get_apic_tpr(DeviceState *d) { + APICCommonState *s; + APICCommonClass *info; + + if (!d) { + return 0; + } + + s = APIC_COMMON(d); + info = APIC_COMMON_GET_CLASS(s); + + return info->get_tpr(s); +} + +void apic_enable_tpr_access_reporting(DeviceState *d, bool enable) +{ APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - return s ? s->tpr >> 4 : 0; + apic_report_tpr_access = enable; + if (info->enable_tpr_reporting) { + info->enable_tpr_reporting(s, enable); + } +} + +void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); + + s->vapic_paddr = paddr; + info->vapic_base_update(s); +} + +void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, + TPRAccess access) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + + vapic_report_tpr_access(s->vapic, s->cpu_env, ip, access); } void apic_report_irq_delivered(int delivered) @@ -166,12 +204,16 @@ void apic_init_reset(DeviceState *d) static void apic_reset_common(DeviceState *d) { APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); bool bsp; bsp = cpu_is_bsp(s->cpu_env); s->apicbase = 0xfee00000 | (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE; + s->vapic_paddr = 0; + info->vapic_base_update(s); + apic_init_reset(d); if (bsp) { @@ -234,6 +276,7 @@ static int apic_init_common(SysBusDevice *dev) { APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info; + static DeviceState *vapic; static int apic_no; if (apic_no >= MAX_APICS) { @@ -244,10 +287,29 @@ static int apic_init_common(SysBusDevice *dev) info = APIC_COMMON_GET_CLASS(s); info->init(s); - sysbus_init_mmio(&s->busdev, &s->io_memory); + sysbus_init_mmio(dev, &s->io_memory); + + if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK) { + vapic = sysbus_create_simple("kvmvapic", -1, NULL); + } + s->vapic = vapic; + if (apic_report_tpr_access && info->enable_tpr_reporting) { + info->enable_tpr_reporting(s, true); + } + return 0; } +static void apic_dispatch_pre_save(void *opaque) +{ + APICCommonState *s = APIC_COMMON(opaque); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); + + if (info->pre_save) { + info->pre_save(s); + } +} + static int apic_dispatch_post_load(void *opaque, int version_id) { APICCommonState *s = APIC_COMMON(opaque); @@ -265,6 +327,7 @@ static const VMStateDescription vmstate_apic_common = { .minimum_version_id = 3, .minimum_version_id_old = 1, .load_state_old = apic_load_old, + .pre_save = apic_dispatch_pre_save, .post_load = apic_dispatch_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(apicbase, APICCommonState), @@ -294,6 +357,8 @@ static const VMStateDescription vmstate_apic_common = { static Property apic_properties_common[] = { DEFINE_PROP_UINT8("id", APICCommonState, id, -1), DEFINE_PROP_PTR("cpu_env", APICCommonState, cpu_env), + DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT, + true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/apic_internal.h b/hw/apic_internal.h index 0cab010717..60a6a8bdae 100644 --- a/hw/apic_internal.h +++ b/hw/apic_internal.h @@ -61,6 +61,9 @@ #define APIC_SV_DIRECTED_IO (1<<12) #define APIC_SV_ENABLE (1<<8) +#define VAPIC_ENABLE_BIT 0 +#define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT) + #define MAX_APICS 255 #define MSI_SPACE_SIZE 0x100000 @@ -82,7 +85,11 @@ typedef struct APICCommonClass void (*init)(APICCommonState *s); void (*set_base)(APICCommonState *s, uint64_t val); void (*set_tpr)(APICCommonState *s, uint8_t val); + uint8_t (*get_tpr)(APICCommonState *s); + void (*enable_tpr_reporting)(APICCommonState *s, bool enable); + void (*vapic_base_update)(APICCommonState *s); void (*external_nmi)(APICCommonState *s); + void (*pre_save)(APICCommonState *s); void (*post_load)(APICCommonState *s); } APICCommonClass; @@ -114,9 +121,29 @@ struct APICCommonState { int64_t timer_expiry; int sipi_vector; int wait_for_sipi; + + uint32_t vapic_control; + DeviceState *vapic; + target_phys_addr_t vapic_paddr; /* note: persistence via kvmvapic */ }; +typedef struct VAPICState { + uint8_t tpr; + uint8_t isr; + uint8_t zero; + uint8_t irr; + uint8_t enabled; +} QEMU_PACKED VAPICState; + +extern bool apic_report_tpr_access; + void apic_report_irq_delivered(int delivered); bool apic_next_timer(APICCommonState *s, int64_t current_time); +void apic_enable_tpr_access_reporting(DeviceState *d, bool enable); +void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr); +void apic_poll_irq(DeviceState *d); + +void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, + TPRAccess access); #endif /* !QEMU_APIC_INTERNAL_H */ diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 306013aeeb..2f46e214cf 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -16,7 +16,7 @@ /* The CPU is also modeled as an interrupt controller. */ #define ARM_PIC_CPU_IRQ 0 #define ARM_PIC_CPU_FIQ 1 -qemu_irq *arm_pic_init_cpu(CPUState *env); +qemu_irq *arm_pic_init_cpu(CPUARMState *env); /* armv7m.c */ qemu_irq *armv7m_init(MemoryRegion *address_space_mem, @@ -29,6 +29,7 @@ struct arm_boot_info { const char *kernel_filename; const char *kernel_cmdline; const char *initrd_filename; + const char *dtb_filename; target_phys_addr_t loader_start; /* multicore boards that use the default secondary core boot functions * need to put the address of the secondary boot code, the boot reg, @@ -49,16 +50,16 @@ struct arm_boot_info { * perform any necessary CPU reset handling and set the PC for thei * secondary CPUs to point at this boot blob. */ - void (*write_secondary_boot)(CPUState *env, + void (*write_secondary_boot)(CPUARMState *env, const struct arm_boot_info *info); - void (*secondary_cpu_reset_hook)(CPUState *env, + void (*secondary_cpu_reset_hook)(CPUARMState *env, const struct arm_boot_info *info); /* Used internally by arm_boot.c */ int is_linux; target_phys_addr_t initrd_size; target_phys_addr_t entry; }; -void arm_load_kernel(CPUState *env, struct arm_boot_info *info); +void arm_load_kernel(CPUARMState *env, struct arm_boot_info *info); /* Multiplication factor to convert from system clock ticks to qemu timer ticks. */ diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 102348bb70..ba6a89d3ed 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -42,7 +42,6 @@ static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset, { mpcore_priv_state *s = (mpcore_priv_state *)opaque; int id; - offset &= 0xff; /* SCU */ switch (offset) { case 0x00: /* Control. */ @@ -63,7 +62,6 @@ static void mpcore_scu_write(void *opaque, target_phys_addr_t offset, uint64_t value, unsigned size) { mpcore_priv_state *s = (mpcore_priv_state *)opaque; - offset &= 0xff; /* SCU */ switch (offset) { case 0: /* Control register. */ @@ -202,16 +200,7 @@ static int realview_mpcore_init(SysBusDevice *dev) } static Property mpcore_rirq_properties[] = { - DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), - /* The ARM11 MPCORE TRM says the on-chip controller may have - * anything from 0 to 224 external interrupt IRQ lines (with another - * 32 internal). We default to 32+32, which is the number provided by - * the ARM11 MPCore test chip in the Realview Versatile Express - * coretile. Other boards may differ and should set this property - * appropriately. Some Linux kernels may not boot if the hardware - * has more IRQ lines than the kernel expects. - */ - DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64), + DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), }; @@ -233,6 +222,15 @@ static TypeInfo mpcore_rirq_info = { static Property mpcore_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), + /* The ARM11 MPCORE TRM says the on-chip controller may have + * anything from 0 to 224 external interrupt IRQ lines (with another + * 32 internal). We default to 32+32, which is the number provided by + * the ARM11 MPCore test chip in the Realview Versatile Express + * coretile. Other boards may differ and should set this property + * appropriately. Some Linux kernels may not boot if the hardware + * has more IRQ lines than the kernel expects. + */ + DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm_boot.c b/hw/arm_boot.c index 2ef25ca9dd..7447f5c169 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -7,11 +7,14 @@ * This code is licensed under the GPL. */ +#include "config.h" #include "hw.h" #include "arm-misc.h" #include "sysemu.h" +#include "boards.h" #include "loader.h" #include "elf.h" +#include "device_tree.h" #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 @@ -56,7 +59,7 @@ static uint32_t smpboot[] = { 0 /* bootreg: Boot register address is held here */ }; -static void default_write_secondary(CPUState *env, +static void default_write_secondary(CPUARMState *env, const struct arm_boot_info *info) { int n; @@ -69,7 +72,7 @@ static void default_write_secondary(CPUState *env, info->smp_loader_start); } -static void default_reset_secondary(CPUState *env, +static void default_reset_secondary(CPUARMState *env, const struct arm_boot_info *info) { stl_phys_notdirty(info->smp_bootreg_addr, 0); @@ -208,12 +211,73 @@ static void set_kernel_args_old(const struct arm_boot_info *info) } } +static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo) +{ +#ifdef CONFIG_FDT + uint32_t mem_reg_property[] = { cpu_to_be32(binfo->loader_start), + cpu_to_be32(binfo->ram_size) }; + void *fdt = NULL; + char *filename; + int size, rc; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); + if (!filename) { + fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); + return -1; + } + + fdt = load_device_tree(filename, &size); + if (!fdt) { + fprintf(stderr, "Couldn't open dtb file %s\n", filename); + g_free(filename); + return -1; + } + g_free(filename); + + rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); + if (rc < 0) { + fprintf(stderr, "couldn't set /memory/reg\n"); + } + + rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + binfo->kernel_cmdline); + if (rc < 0) { + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + } + + if (binfo->initrd_size) { + rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", + binfo->loader_start + INITRD_LOAD_ADDR); + if (rc < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); + } + + rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", + binfo->loader_start + INITRD_LOAD_ADDR + + binfo->initrd_size); + if (rc < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); + } + } + + cpu_physical_memory_write(addr, fdt, size); + + return 0; + +#else + fprintf(stderr, "Device tree requested, " + "but qemu was compiled without fdt support\n"); + return -1; +#endif +} + static void do_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUARMState *env = opaque; const struct arm_boot_info *info = env->boot_info; - cpu_reset(env); + cpu_state_reset(env); if (info) { if (!info->is_linux) { /* Jump to the entry point. */ @@ -222,10 +286,12 @@ static void do_cpu_reset(void *opaque) } else { if (env == first_cpu) { env->regs[15] = info->loader_start; - if (old_param) { - set_kernel_args_old(info); - } else { - set_kernel_args(info); + if (!info->dtb_filename) { + if (old_param) { + set_kernel_args_old(info); + } else { + set_kernel_args(info); + } } } else { info->secondary_cpu_reset_hook(env, info); @@ -234,7 +300,7 @@ static void do_cpu_reset(void *opaque) } } -void arm_load_kernel(CPUState *env, struct arm_boot_info *info) +void arm_load_kernel(CPUARMState *env, struct arm_boot_info *info) { int kernel_size; int initrd_size; @@ -243,6 +309,7 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) uint64_t elf_entry; target_phys_addr_t entry; int big_endian; + QemuOpts *machine_opts; /* Load the kernel. */ if (!info->kernel_filename) { @@ -250,6 +317,13 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) exit(1); } + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); + if (machine_opts) { + info->dtb_filename = qemu_opt_get(machine_opts, "dtb"); + } else { + info->dtb_filename = NULL; + } + if (!info->secondary_cpu_reset_hook) { info->secondary_cpu_reset_hook = default_reset_secondary; } @@ -300,8 +374,25 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) } else { initrd_size = 0; } + info->initrd_size = initrd_size; + bootloader[4] = info->board_id; - bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR; + + /* for device tree boot, we pass the DTB directly in r2. Otherwise + * we point to the kernel args. + */ + if (info->dtb_filename) { + /* Place the DTB after the initrd in memory */ + target_phys_addr_t dtb_start = TARGET_PAGE_ALIGN(info->loader_start + + INITRD_LOAD_ADDR + + initrd_size); + if (load_dtb(dtb_start, info)) { + exit(1); + } + bootloader[5] = dtb_start; + } else { + bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR; + } bootloader[6] = entry; for (n = 0; n < sizeof(bootloader) / 4; n++) { bootloader[n] = tswap32(bootloader[n]); @@ -311,7 +402,6 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) if (info->nb_cpus > 1) { info->write_secondary_boot(env, info); } - info->initrd_size = initrd_size; } info->is_linux = is_linux; diff --git a/hw/arm_gic.c b/hw/arm_gic.c index cf582a5a14..6b34c06a8f 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -13,6 +13,8 @@ /* Maximum number of possible interrupts, determined by the GIC architecture */ #define GIC_MAXIRQ 1020 +/* First 32 are private to each CPU (SGIs and PPIs). */ +#define GIC_INTERNAL 32 //#define DEBUG_GIC #ifdef DEBUG_GIC @@ -73,8 +75,9 @@ typedef struct gic_irq_state #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger -#define GIC_GET_PRIORITY(irq, cpu) \ - (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32]) +#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ + s->priority1[irq][cpu] : \ + s->priority2[(irq) - GIC_INTERNAL]) #ifdef NVIC #define GIC_TARGET(irq) 1 #else @@ -92,8 +95,8 @@ typedef struct gic_state #ifndef NVIC int irq_target[GIC_MAXIRQ]; #endif - int priority1[32][NCPU]; - int priority2[GIC_MAXIRQ - 32]; + int priority1[GIC_INTERNAL][NCPU]; + int priority2[GIC_MAXIRQ - GIC_INTERNAL]; int last_active[GIC_MAXIRQ][NCPU]; int priority_mask[NCPU]; @@ -174,7 +177,7 @@ static void gic_set_irq(void *opaque, int irq, int level) { gic_state *s = (gic_state *)opaque; /* The first external input line is internal interrupt 32. */ - irq += 32; + irq += GIC_INTERNAL; if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK)) return; @@ -316,7 +319,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (irq >= s->num_irq) goto bad_reg; res = 0; - mask = (irq < 32) ? cm : ALL_CPU_MASK; + mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { if (GIC_TEST_PENDING(irq + i, mask)) { res |= (1 << i); @@ -328,7 +331,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (irq >= s->num_irq) goto bad_reg; res = 0; - mask = (irq < 32) ? cm : ALL_CPU_MASK; + mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { if (GIC_TEST_ACTIVE(irq + i, mask)) { res |= (1 << i); @@ -435,8 +438,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, value = 0xff; for (i = 0; i < 8; i++) { if (value & (1 << i)) { - int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq); - int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK; + int mask = (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq); + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (!GIC_TEST_ENABLED(irq + i, cm)) { DPRINTF("Enabled IRQ %d\n", irq + i); @@ -460,7 +463,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, value = 0; for (i = 0; i < 8; i++) { if (value & (1 << i)) { - int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK; + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (GIC_TEST_ENABLED(irq + i, cm)) { DPRINTF("Disabled IRQ %d\n", irq + i); @@ -502,10 +505,10 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, irq = (offset - 0x400) + GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; - if (irq < 32) { + if (irq < GIC_INTERNAL) { s->priority1[irq][cpu] = value; } else { - s->priority2[irq - 32] = value; + s->priority2[irq - GIC_INTERNAL] = value; } #ifndef NVIC } else if (offset < 0xc00) { @@ -515,7 +518,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, goto bad_reg; if (irq < 29) value = 0; - else if (irq < 32) + else if (irq < GIC_INTERNAL) value = ALL_CPU_MASK; s->irq_target[irq] = value & ALL_CPU_MASK; } else if (offset < 0xf00) { @@ -523,7 +526,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; - if (irq < 32) + if (irq < GIC_INTERNAL) value |= 0xaa; for (i = 0; i < 4; i++) { if (value & (1 << (i * 2))) { @@ -655,14 +658,14 @@ static uint64_t gic_thiscpu_read(void *opaque, target_phys_addr_t addr, unsigned size) { gic_state *s = (gic_state *)opaque; - return gic_cpu_read(s, gic_get_current_cpu(), addr & 0xff); + return gic_cpu_read(s, gic_get_current_cpu(), addr); } static void gic_thiscpu_write(void *opaque, target_phys_addr_t addr, uint64_t value, unsigned size) { gic_state *s = (gic_state *)opaque; - gic_cpu_write(s, gic_get_current_cpu(), addr & 0xff, value); + gic_cpu_write(s, gic_get_current_cpu(), addr, value); } /* Wrappers to read/write the GIC CPU interface for a specific CPU. @@ -674,7 +677,7 @@ static uint64_t gic_do_cpu_read(void *opaque, target_phys_addr_t addr, gic_state **backref = (gic_state **)opaque; gic_state *s = *backref; int id = (backref - s->backref); - return gic_cpu_read(s, id, addr & 0xff); + return gic_cpu_read(s, id, addr); } static void gic_do_cpu_write(void *opaque, target_phys_addr_t addr, @@ -683,7 +686,7 @@ static void gic_do_cpu_write(void *opaque, target_phys_addr_t addr, gic_state **backref = (gic_state **)opaque; gic_state *s = *backref; int id = (backref - s->backref); - gic_cpu_write(s, id, addr & 0xff, value); + gic_cpu_write(s, id, addr, value); } static const MemoryRegionOps gic_thiscpu_ops = { @@ -736,7 +739,7 @@ static void gic_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->enabled); for (i = 0; i < NUM_CPU(s); i++) { qemu_put_be32(f, s->cpu_enabled[i]); - for (j = 0; j < 32; j++) + for (j = 0; j < GIC_INTERNAL; j++) qemu_put_be32(f, s->priority1[j][i]); for (j = 0; j < s->num_irq; j++) qemu_put_be32(f, s->last_active[j][i]); @@ -745,7 +748,7 @@ static void gic_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->running_priority[i]); qemu_put_be32(f, s->current_pending[i]); } - for (i = 0; i < s->num_irq - 32; i++) { + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { qemu_put_be32(f, s->priority2[i]); } for (i = 0; i < s->num_irq; i++) { @@ -773,7 +776,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) s->enabled = qemu_get_be32(f); for (i = 0; i < NUM_CPU(s); i++) { s->cpu_enabled[i] = qemu_get_be32(f); - for (j = 0; j < 32; j++) + for (j = 0; j < GIC_INTERNAL; j++) s->priority1[j][i] = qemu_get_be32(f); for (j = 0; j < s->num_irq; j++) s->last_active[j][i] = qemu_get_be32(f); @@ -782,7 +785,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) s->running_priority[i] = qemu_get_be32(f); s->current_pending[i] = qemu_get_be32(f); } - for (i = 0; i < s->num_irq - 32; i++) { + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { s->priority2[i] = qemu_get_be32(f); } for (i = 0; i < s->num_irq; i++) { @@ -816,7 +819,16 @@ static void gic_init(gic_state *s, int num_irq) hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", num_irq, GIC_MAXIRQ); } - qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - 32); + /* ITLinesNumber is represented as (N / 32) - 1 (see + * gic_dist_readb) so this is an implementation imposed + * restriction, not an architectural one: + */ + if (s->num_irq < 32 || (s->num_irq % 32)) { + hw_error("%d interrupt lines unsupported: not divisible by 32\n", + num_irq); + } + + qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL); for (i = 0; i < NUM_CPU(s); i++) { sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c index 361e887dec..df7fb4c9bd 100644 --- a/hw/arm_mptimer.c +++ b/hw/arm_mptimer.c @@ -97,7 +97,6 @@ static uint64_t timerblock_read(void *opaque, target_phys_addr_t addr, { timerblock *tb = (timerblock *)opaque; int64_t val; - addr &= 0x1f; switch (addr) { case 0: /* Load */ return tb->load; @@ -126,7 +125,6 @@ static void timerblock_write(void *opaque, target_phys_addr_t addr, { timerblock *tb = (timerblock *)opaque; int64_t old; - addr &= 0x1f; switch (addr) { case 0: /* Load */ tb->load = value; diff --git a/hw/arm_pic.c b/hw/arm_pic.c index a2e8a73301..109496528c 100644 --- a/hw/arm_pic.c +++ b/hw/arm_pic.c @@ -13,7 +13,7 @@ /* Input 0 is IRQ and input 1 is FIQ. */ static void arm_pic_cpu_handler(void *opaque, int irq, int level) { - CPUState *env = (CPUState *)opaque; + CPUARMState *env = (CPUARMState *)opaque; switch (irq) { case ARM_PIC_CPU_IRQ: if (level) @@ -32,7 +32,7 @@ static void arm_pic_cpu_handler(void *opaque, int irq, int level) } } -qemu_irq *arm_pic_init_cpu(CPUState *env) +qemu_irq *arm_pic_init_cpu(CPUARMState *env) { return qemu_allocate_irqs(arm_pic_cpu_handler, env, 2); } diff --git a/hw/armv7m.c b/hw/armv7m.c index 6b805798e6..4aac076e48 100644 --- a/hw/armv7m.c +++ b/hw/armv7m.c @@ -149,7 +149,7 @@ static void armv7m_bitband_init(void) static void armv7m_reset(void *opaque) { - cpu_reset((CPUState *)opaque); + cpu_state_reset((CPUARMState *)opaque); } /* Init CPU and memory for a v7-M based board. @@ -160,7 +160,7 @@ qemu_irq *armv7m_init(MemoryRegion *address_space_mem, int flash_size, int sram_size, const char *kernel_filename, const char *cpu_model) { - CPUState *env; + CPUARMState *env; DeviceState *nvic; /* FIXME: make this local state. */ static qemu_irq pic[64]; diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c index c9301fda1d..2304e3533a 100644 --- a/hw/axis_dev88.c +++ b/hw/axis_dev88.c @@ -247,7 +247,7 @@ void axisdev88_init (ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUCRISState *env; DeviceState *dev; SysBusDevice *s; DriveInfo *nand; diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c new file mode 100644 index 0000000000..e2140aea2b --- /dev/null +++ b/hw/cadence_gem.c @@ -0,0 +1,1233 @@ +/* + * QEMU Xilinx GEM emulation + * + * Copyright (c) 2011 Xilinx, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <zlib.h> /* For crc32 */ + +#include "sysbus.h" +#include "net.h" +#include "net/checksum.h" + +#ifdef CADENCE_GEM_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define GEM_NWCTRL (0x00000000/4) /* Network Control reg */ +#define GEM_NWCFG (0x00000004/4) /* Network Config reg */ +#define GEM_NWSTATUS (0x00000008/4) /* Network Status reg */ +#define GEM_USERIO (0x0000000C/4) /* User IO reg */ +#define GEM_DMACFG (0x00000010/4) /* DMA Control reg */ +#define GEM_TXSTATUS (0x00000014/4) /* TX Status reg */ +#define GEM_RXQBASE (0x00000018/4) /* RX Q Base address reg */ +#define GEM_TXQBASE (0x0000001C/4) /* TX Q Base address reg */ +#define GEM_RXSTATUS (0x00000020/4) /* RX Status reg */ +#define GEM_ISR (0x00000024/4) /* Interrupt Status reg */ +#define GEM_IER (0x00000028/4) /* Interrupt Enable reg */ +#define GEM_IDR (0x0000002C/4) /* Interrupt Disable reg */ +#define GEM_IMR (0x00000030/4) /* Interrupt Mask reg */ +#define GEM_PHYMNTNC (0x00000034/4) /* Phy Maintaince reg */ +#define GEM_RXPAUSE (0x00000038/4) /* RX Pause Time reg */ +#define GEM_TXPAUSE (0x0000003C/4) /* TX Pause Time reg */ +#define GEM_TXPARTIALSF (0x00000040/4) /* TX Partial Store and Forward */ +#define GEM_RXPARTIALSF (0x00000044/4) /* RX Partial Store and Forward */ +#define GEM_HASHLO (0x00000080/4) /* Hash Low address reg */ +#define GEM_HASHHI (0x00000084/4) /* Hash High address reg */ +#define GEM_SPADDR1LO (0x00000088/4) /* Specific addr 1 low reg */ +#define GEM_SPADDR1HI (0x0000008C/4) /* Specific addr 1 high reg */ +#define GEM_SPADDR2LO (0x00000090/4) /* Specific addr 2 low reg */ +#define GEM_SPADDR2HI (0x00000094/4) /* Specific addr 2 high reg */ +#define GEM_SPADDR3LO (0x00000098/4) /* Specific addr 3 low reg */ +#define GEM_SPADDR3HI (0x0000009C/4) /* Specific addr 3 high reg */ +#define GEM_SPADDR4LO (0x000000A0/4) /* Specific addr 4 low reg */ +#define GEM_SPADDR4HI (0x000000A4/4) /* Specific addr 4 high reg */ +#define GEM_TIDMATCH1 (0x000000A8/4) /* Type ID1 Match reg */ +#define GEM_TIDMATCH2 (0x000000AC/4) /* Type ID2 Match reg */ +#define GEM_TIDMATCH3 (0x000000B0/4) /* Type ID3 Match reg */ +#define GEM_TIDMATCH4 (0x000000B4/4) /* Type ID4 Match reg */ +#define GEM_WOLAN (0x000000B8/4) /* Wake on LAN reg */ +#define GEM_IPGSTRETCH (0x000000BC/4) /* IPG Stretch reg */ +#define GEM_SVLAN (0x000000C0/4) /* Stacked VLAN reg */ +#define GEM_MODID (0x000000FC/4) /* Module ID reg */ +#define GEM_OCTTXLO (0x00000100/4) /* Octects transmitted Low reg */ +#define GEM_OCTTXHI (0x00000104/4) /* Octects transmitted High reg */ +#define GEM_TXCNT (0x00000108/4) /* Error-free Frames transmitted */ +#define GEM_TXBCNT (0x0000010C/4) /* Error-free Broadcast Frames */ +#define GEM_TXMCNT (0x00000110/4) /* Error-free Multicast Frame */ +#define GEM_TXPAUSECNT (0x00000114/4) /* Pause Frames Transmitted */ +#define GEM_TX64CNT (0x00000118/4) /* Error-free 64 TX */ +#define GEM_TX65CNT (0x0000011C/4) /* Error-free 65-127 TX */ +#define GEM_TX128CNT (0x00000120/4) /* Error-free 128-255 TX */ +#define GEM_TX256CNT (0x00000124/4) /* Error-free 256-511 */ +#define GEM_TX512CNT (0x00000128/4) /* Error-free 512-1023 TX */ +#define GEM_TX1024CNT (0x0000012C/4) /* Error-free 1024-1518 TX */ +#define GEM_TX1519CNT (0x00000130/4) /* Error-free larger than 1519 TX */ +#define GEM_TXURUNCNT (0x00000134/4) /* TX under run error counter */ +#define GEM_SINGLECOLLCNT (0x00000138/4) /* Single Collision Frames */ +#define GEM_MULTCOLLCNT (0x0000013C/4) /* Multiple Collision Frames */ +#define GEM_EXCESSCOLLCNT (0x00000140/4) /* Excessive Collision Frames */ +#define GEM_LATECOLLCNT (0x00000144/4) /* Late Collision Frames */ +#define GEM_DEFERTXCNT (0x00000148/4) /* Deferred Transmission Frames */ +#define GEM_CSENSECNT (0x0000014C/4) /* Carrier Sense Error Counter */ +#define GEM_OCTRXLO (0x00000150/4) /* Octects Received register Low */ +#define GEM_OCTRXHI (0x00000154/4) /* Octects Received register High */ +#define GEM_RXCNT (0x00000158/4) /* Error-free Frames Received */ +#define GEM_RXBROADCNT (0x0000015C/4) /* Error-free Broadcast Frames RX */ +#define GEM_RXMULTICNT (0x00000160/4) /* Error-free Multicast Frames RX */ +#define GEM_RXPAUSECNT (0x00000164/4) /* Pause Frames Received Counter */ +#define GEM_RX64CNT (0x00000168/4) /* Error-free 64 byte Frames RX */ +#define GEM_RX65CNT (0x0000016C/4) /* Error-free 65-127B Frames RX */ +#define GEM_RX128CNT (0x00000170/4) /* Error-free 128-255B Frames RX */ +#define GEM_RX256CNT (0x00000174/4) /* Error-free 256-512B Frames RX */ +#define GEM_RX512CNT (0x00000178/4) /* Error-free 512-1023B Frames RX */ +#define GEM_RX1024CNT (0x0000017C/4) /* Error-free 1024-1518B Frames RX */ +#define GEM_RX1519CNT (0x00000180/4) /* Error-free 1519-max Frames RX */ +#define GEM_RXUNDERCNT (0x00000184/4) /* Undersize Frames Received */ +#define GEM_RXOVERCNT (0x00000188/4) /* Oversize Frames Received */ +#define GEM_RXJABCNT (0x0000018C/4) /* Jabbers Received Counter */ +#define GEM_RXFCSCNT (0x00000190/4) /* Frame Check seq. Error Counter */ +#define GEM_RXLENERRCNT (0x00000194/4) /* Length Field Error Counter */ +#define GEM_RXSYMERRCNT (0x00000198/4) /* Symbol Error Counter */ +#define GEM_RXALIGNERRCNT (0x0000019C/4) /* Alignment Error Counter */ +#define GEM_RXRSCERRCNT (0x000001A0/4) /* Receive Resource Error Counter */ +#define GEM_RXORUNCNT (0x000001A4/4) /* Receive Overrun Counter */ +#define GEM_RXIPCSERRCNT (0x000001A8/4) /* IP header Checksum Error Counter */ +#define GEM_RXTCPCCNT (0x000001AC/4) /* TCP Checksum Error Counter */ +#define GEM_RXUDPCCNT (0x000001B0/4) /* UDP Checksum Error Counter */ + +#define GEM_1588S (0x000001D0/4) /* 1588 Timer Seconds */ +#define GEM_1588NS (0x000001D4/4) /* 1588 Timer Nanoseconds */ +#define GEM_1588ADJ (0x000001D8/4) /* 1588 Timer Adjust */ +#define GEM_1588INC (0x000001DC/4) /* 1588 Timer Increment */ +#define GEM_PTPETXS (0x000001E0/4) /* PTP Event Frame Transmitted (s) */ +#define GEM_PTPETXNS (0x000001E4/4) /* PTP Event Frame Transmitted (ns) */ +#define GEM_PTPERXS (0x000001E8/4) /* PTP Event Frame Received (s) */ +#define GEM_PTPERXNS (0x000001EC/4) /* PTP Event Frame Received (ns) */ +#define GEM_PTPPTXS (0x000001E0/4) /* PTP Peer Frame Transmitted (s) */ +#define GEM_PTPPTXNS (0x000001E4/4) /* PTP Peer Frame Transmitted (ns) */ +#define GEM_PTPPRXS (0x000001E8/4) /* PTP Peer Frame Received (s) */ +#define GEM_PTPPRXNS (0x000001EC/4) /* PTP Peer Frame Received (ns) */ + +/* Design Configuration Registers */ +#define GEM_DESCONF (0x00000280/4) +#define GEM_DESCONF2 (0x00000284/4) +#define GEM_DESCONF3 (0x00000288/4) +#define GEM_DESCONF4 (0x0000028C/4) +#define GEM_DESCONF5 (0x00000290/4) +#define GEM_DESCONF6 (0x00000294/4) +#define GEM_DESCONF7 (0x00000298/4) + +#define GEM_MAXREG (0x00000640/4) /* Last valid GEM address */ + +/*****************************************/ +#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */ +#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */ +#define GEM_NWCTRL_RXENA 0x00000004 /* Receive Enable */ +#define GEM_NWCTRL_LOCALLOOP 0x00000002 /* Local Loopback */ + +#define GEM_NWCFG_STRIP_FCS 0x00020000 /* Strip FCS field */ +#define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with lenth err */ +#define GEM_NWCFG_BUFF_OFST_M 0x0000C000 /* Receive buffer offset mask */ +#define GEM_NWCFG_BUFF_OFST_S 14 /* Receive buffer offset shift */ +#define GEM_NWCFG_UCAST_HASH 0x00000080 /* accept unicast if hash match */ +#define GEM_NWCFG_MCAST_HASH 0x00000040 /* accept multicast if hash match */ +#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */ +#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */ + +#define GEM_DMACFG_RBUFSZ_M 0x007F0000 /* DMA RX Buffer Size mask */ +#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */ +#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ +#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */ + +#define GEM_TXSTATUS_TXCMPL 0x00000020 /* Transmit Complete */ +#define GEM_TXSTATUS_USED 0x00000001 /* sw owned descriptor encountered */ + +#define GEM_RXSTATUS_FRMRCVD 0x00000002 /* Frame received */ +#define GEM_RXSTATUS_NOBUF 0x00000001 /* Buffer unavailable */ + +/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */ +#define GEM_INT_TXCMPL 0x00000080 /* Transmit Complete */ +#define GEM_INT_TXUSED 0x00000008 +#define GEM_INT_RXUSED 0x00000004 +#define GEM_INT_RXCMPL 0x00000002 + +#define GEM_PHYMNTNC_OP_R 0x20000000 /* read operation */ +#define GEM_PHYMNTNC_OP_W 0x10000000 /* write operation */ +#define GEM_PHYMNTNC_ADDR 0x0F800000 /* Address bits */ +#define GEM_PHYMNTNC_ADDR_SHFT 23 +#define GEM_PHYMNTNC_REG 0x007C0000 /* register bits */ +#define GEM_PHYMNTNC_REG_SHIFT 18 + +/* Marvell PHY definitions */ +#define BOARD_PHY_ADDRESS 23 /* PHY address we will emulate a device at */ + +#define PHY_REG_CONTROL 0 +#define PHY_REG_STATUS 1 +#define PHY_REG_PHYID1 2 +#define PHY_REG_PHYID2 3 +#define PHY_REG_ANEGADV 4 +#define PHY_REG_LINKPABIL 5 +#define PHY_REG_ANEGEXP 6 +#define PHY_REG_NEXTP 7 +#define PHY_REG_LINKPNEXTP 8 +#define PHY_REG_100BTCTRL 9 +#define PHY_REG_1000BTSTAT 10 +#define PHY_REG_EXTSTAT 15 +#define PHY_REG_PHYSPCFC_CTL 16 +#define PHY_REG_PHYSPCFC_ST 17 +#define PHY_REG_INT_EN 18 +#define PHY_REG_INT_ST 19 +#define PHY_REG_EXT_PHYSPCFC_CTL 20 +#define PHY_REG_RXERR 21 +#define PHY_REG_EACD 22 +#define PHY_REG_LED 24 +#define PHY_REG_LED_OVRD 25 +#define PHY_REG_EXT_PHYSPCFC_CTL2 26 +#define PHY_REG_EXT_PHYSPCFC_ST 27 +#define PHY_REG_CABLE_DIAG 28 + +#define PHY_REG_CONTROL_RST 0x8000 +#define PHY_REG_CONTROL_LOOP 0x4000 +#define PHY_REG_CONTROL_ANEG 0x1000 + +#define PHY_REG_STATUS_LINK 0x0004 +#define PHY_REG_STATUS_ANEGCMPL 0x0020 + +#define PHY_REG_INT_ST_ANEGCMPL 0x0800 +#define PHY_REG_INT_ST_LINKC 0x0400 +#define PHY_REG_INT_ST_ENERGY 0x0010 + +/***********************************************************************/ +#define GEM_RX_REJECT 1 +#define GEM_RX_ACCEPT 0 + +/***********************************************************************/ + +#define DESC_1_USED 0x80000000 +#define DESC_1_LENGTH 0x00001FFF + +#define DESC_1_TX_WRAP 0x40000000 +#define DESC_1_TX_LAST 0x00008000 + +#define DESC_0_RX_WRAP 0x00000002 +#define DESC_0_RX_OWNERSHIP 0x00000001 + +#define DESC_1_RX_SOF 0x00004000 +#define DESC_1_RX_EOF 0x00008000 + +static inline unsigned tx_desc_get_buffer(unsigned *desc) +{ + return desc[0]; +} + +static inline unsigned tx_desc_get_used(unsigned *desc) +{ + return (desc[1] & DESC_1_USED) ? 1 : 0; +} + +static inline void tx_desc_set_used(unsigned *desc) +{ + desc[1] |= DESC_1_USED; +} + +static inline unsigned tx_desc_get_wrap(unsigned *desc) +{ + return (desc[1] & DESC_1_TX_WRAP) ? 1 : 0; +} + +static inline unsigned tx_desc_get_last(unsigned *desc) +{ + return (desc[1] & DESC_1_TX_LAST) ? 1 : 0; +} + +static inline unsigned tx_desc_get_length(unsigned *desc) +{ + return desc[1] & DESC_1_LENGTH; +} + +static inline void print_gem_tx_desc(unsigned *desc) +{ + DB_PRINT("TXDESC:\n"); + DB_PRINT("bufaddr: 0x%08x\n", *desc); + DB_PRINT("used_hw: %d\n", tx_desc_get_used(desc)); + DB_PRINT("wrap: %d\n", tx_desc_get_wrap(desc)); + DB_PRINT("last: %d\n", tx_desc_get_last(desc)); + DB_PRINT("length: %d\n", tx_desc_get_length(desc)); +} + +static inline unsigned rx_desc_get_buffer(unsigned *desc) +{ + return desc[0] & ~0x3UL; +} + +static inline unsigned rx_desc_get_wrap(unsigned *desc) +{ + return desc[0] & DESC_0_RX_WRAP ? 1 : 0; +} + +static inline unsigned rx_desc_get_ownership(unsigned *desc) +{ + return desc[0] & DESC_0_RX_OWNERSHIP ? 1 : 0; +} + +static inline void rx_desc_set_ownership(unsigned *desc) +{ + desc[0] |= DESC_0_RX_OWNERSHIP; +} + +static inline void rx_desc_set_sof(unsigned *desc) +{ + desc[1] |= DESC_1_RX_SOF; +} + +static inline void rx_desc_set_eof(unsigned *desc) +{ + desc[1] |= DESC_1_RX_EOF; +} + +static inline void rx_desc_set_length(unsigned *desc, unsigned len) +{ + desc[1] &= ~DESC_1_LENGTH; + desc[1] |= len; +} + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + NICState *nic; + NICConf conf; + qemu_irq irq; + + /* GEM registers backing store */ + uint32_t regs[GEM_MAXREG]; + /* Mask of register bits which are write only */ + uint32_t regs_wo[GEM_MAXREG]; + /* Mask of register bits which are read only */ + uint32_t regs_ro[GEM_MAXREG]; + /* Mask of register bits which are clear on read */ + uint32_t regs_rtc[GEM_MAXREG]; + /* Mask of register bits which are write 1 to clear */ + uint32_t regs_w1c[GEM_MAXREG]; + + /* PHY registers backing store */ + uint16_t phy_regs[32]; + + uint8_t phy_loop; /* Are we in phy loopback? */ + + /* The current DMA descriptor pointers */ + target_phys_addr_t rx_desc_addr; + target_phys_addr_t tx_desc_addr; + +} GemState; + +/* The broadcast MAC address: 0xFFFFFFFFFFFF */ +const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +/* + * gem_init_register_masks: + * One time initialization. + * Set masks to identify which register bits have magical clear properties + */ +static void gem_init_register_masks(GemState *s) +{ + /* Mask of register bits which are read only*/ + memset(&s->regs_ro[0], 0, sizeof(s->regs_ro)); + s->regs_ro[GEM_NWCTRL] = 0xFFF80000; + s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF; + s->regs_ro[GEM_DMACFG] = 0xFE00F000; + s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08; + s->regs_ro[GEM_RXQBASE] = 0x00000003; + s->regs_ro[GEM_TXQBASE] = 0x00000003; + s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0; + s->regs_ro[GEM_ISR] = 0xFFFFFFFF; + s->regs_ro[GEM_IMR] = 0xFFFFFFFF; + s->regs_ro[GEM_MODID] = 0xFFFFFFFF; + + /* Mask of register bits which are clear on read */ + memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc)); + s->regs_rtc[GEM_ISR] = 0xFFFFFFFF; + + /* Mask of register bits which are write 1 to clear */ + memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c)); + s->regs_w1c[GEM_TXSTATUS] = 0x000001F7; + s->regs_w1c[GEM_RXSTATUS] = 0x0000000F; + + /* Mask of register bits which are write only */ + memset(&s->regs_wo[0], 0, sizeof(s->regs_wo)); + s->regs_wo[GEM_NWCTRL] = 0x00073E60; + s->regs_wo[GEM_IER] = 0x07FFFFFF; + s->regs_wo[GEM_IDR] = 0x07FFFFFF; +} + +/* + * phy_update_link: + * Make the emulated PHY link state match the QEMU "interface" state. + */ +static void phy_update_link(GemState *s) +{ + DB_PRINT("down %d\n", s->nic->nc.link_down); + + /* Autonegotiation status mirrors link status. */ + if (s->nic->nc.link_down) { + s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL | + PHY_REG_STATUS_LINK); + s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC; + } else { + s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL | + PHY_REG_STATUS_LINK); + s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC | + PHY_REG_INT_ST_ANEGCMPL | + PHY_REG_INT_ST_ENERGY); + } +} + +static int gem_can_receive(VLANClientState *nc) +{ + GemState *s; + + s = DO_UPCAST(NICState, nc, nc)->opaque; + + DB_PRINT("\n"); + + /* Do nothing if receive is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + return 0; + } + + return 1; +} + +/* + * gem_update_int_status: + * Raise or lower interrupt based on current status. + */ +static void gem_update_int_status(GemState *s) +{ + uint32_t new_interrupts = 0; + /* Packet transmitted ? */ + if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_TXCMPL) { + new_interrupts |= GEM_INT_TXCMPL; + } + /* End of TX ring ? */ + if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_USED) { + new_interrupts |= GEM_INT_TXUSED; + } + + /* Frame received ? */ + if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_FRMRCVD) { + new_interrupts |= GEM_INT_RXCMPL; + } + /* RX ring full ? */ + if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_NOBUF) { + new_interrupts |= GEM_INT_RXUSED; + } + + s->regs[GEM_ISR] |= new_interrupts & ~(s->regs[GEM_IMR]); + + if (s->regs[GEM_ISR]) { + DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]); + qemu_set_irq(s->irq, 1); + } else { + qemu_set_irq(s->irq, 0); + } +} + +/* + * gem_receive_updatestats: + * Increment receive statistics. + */ +static void gem_receive_updatestats(GemState *s, const uint8_t *packet, + unsigned bytes) +{ + uint64_t octets; + + /* Total octets (bytes) received */ + octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) | + s->regs[GEM_OCTRXHI]; + octets += bytes; + s->regs[GEM_OCTRXLO] = octets >> 32; + s->regs[GEM_OCTRXHI] = octets; + + /* Error-free Frames received */ + s->regs[GEM_RXCNT]++; + + /* Error-free Broadcast Frames counter */ + if (!memcmp(packet, broadcast_addr, 6)) { + s->regs[GEM_RXBROADCNT]++; + } + + /* Error-free Multicast Frames counter */ + if (packet[0] == 0x01) { + s->regs[GEM_RXMULTICNT]++; + } + + if (bytes <= 64) { + s->regs[GEM_RX64CNT]++; + } else if (bytes <= 127) { + s->regs[GEM_RX65CNT]++; + } else if (bytes <= 255) { + s->regs[GEM_RX128CNT]++; + } else if (bytes <= 511) { + s->regs[GEM_RX256CNT]++; + } else if (bytes <= 1023) { + s->regs[GEM_RX512CNT]++; + } else if (bytes <= 1518) { + s->regs[GEM_RX1024CNT]++; + } else { + s->regs[GEM_RX1519CNT]++; + } +} + +/* + * Get the MAC Address bit from the specified position + */ +static unsigned get_bit(const uint8_t *mac, unsigned bit) +{ + unsigned byte; + + byte = mac[bit / 8]; + byte >>= (bit & 0x7); + byte &= 1; + + return byte; +} + +/* + * Calculate a GEM MAC Address hash index + */ +static unsigned calc_mac_hash(const uint8_t *mac) +{ + int index_bit, mac_bit; + unsigned hash_index; + + hash_index = 0; + mac_bit = 5; + for (index_bit = 5; index_bit >= 0; index_bit--) { + hash_index |= (get_bit(mac, mac_bit) ^ + get_bit(mac, mac_bit + 6) ^ + get_bit(mac, mac_bit + 12) ^ + get_bit(mac, mac_bit + 18) ^ + get_bit(mac, mac_bit + 24) ^ + get_bit(mac, mac_bit + 30) ^ + get_bit(mac, mac_bit + 36) ^ + get_bit(mac, mac_bit + 42)) << index_bit; + mac_bit--; + } + + return hash_index; +} + +/* + * gem_mac_address_filter: + * Accept or reject this destination address? + * Returns: + * GEM_RX_REJECT: reject + * GEM_RX_ACCEPT: accept + */ +static int gem_mac_address_filter(GemState *s, const uint8_t *packet) +{ + uint8_t *gem_spaddr; + int i; + + /* Promiscuous mode? */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) { + return GEM_RX_ACCEPT; + } + + if (!memcmp(packet, broadcast_addr, 6)) { + /* Reject broadcast packets? */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) { + return GEM_RX_REJECT; + } + return GEM_RX_ACCEPT; + } + + /* Accept packets -w- hash match? */ + if ((packet[0] == 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) || + (packet[0] != 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) { + unsigned hash_index; + + hash_index = calc_mac_hash(packet); + if (hash_index < 32) { + if (s->regs[GEM_HASHLO] & (1<<hash_index)) { + return GEM_RX_ACCEPT; + } + } else { + hash_index -= 32; + if (s->regs[GEM_HASHHI] & (1<<hash_index)) { + return GEM_RX_ACCEPT; + } + } + } + + /* Check all 4 specific addresses */ + gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]); + for (i = 0; i < 4; i++) { + if (!memcmp(packet, gem_spaddr, 6)) { + return GEM_RX_ACCEPT; + } + + gem_spaddr += 8; + } + + /* No address match; reject the packet */ + return GEM_RX_REJECT; +} + +/* + * gem_receive: + * Fit a packet handed to us by QEMU into the receive descriptor ring. + */ +static ssize_t gem_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + unsigned desc[2]; + target_phys_addr_t packet_desc_addr, last_desc_addr; + GemState *s; + unsigned rxbufsize, bytes_to_copy; + unsigned rxbuf_offset; + uint8_t rxbuf[2048]; + uint8_t *rxbuf_ptr; + + s = DO_UPCAST(NICState, nc, nc)->opaque; + + /* Do nothing if receive is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + return -1; + } + + /* Is this destination MAC address "for us" ? */ + if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) { + return -1; + } + + /* Discard packets with receive length error enabled ? */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) { + unsigned type_len; + + /* Fish the ethertype / length field out of the RX packet */ + type_len = buf[12] << 8 | buf[13]; + /* It is a length field, not an ethertype */ + if (type_len < 0x600) { + if (size < type_len) { + /* discard */ + return -1; + } + } + } + + /* + * Determine configured receive buffer offset (probably 0) + */ + rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >> + GEM_NWCFG_BUFF_OFST_S; + + /* The configure size of each receive buffer. Determines how many + * buffers needed to hold this packet. + */ + rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> + GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; + bytes_to_copy = size; + + /* Strip of FCS field ? (usually yes) */ + if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) { + rxbuf_ptr = (void *)buf; + } else { + unsigned crc_val; + int crc_offset; + + /* The application wants the FCS field, which QEMU does not provide. + * We must try and caclculate one. + */ + + memcpy(rxbuf, buf, size); + memset(rxbuf + size, 0, sizeof(rxbuf - size)); + rxbuf_ptr = rxbuf; + crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60))); + if (size < 60) { + crc_offset = 60; + } else { + crc_offset = size; + } + memcpy(rxbuf + crc_offset, &crc_val, sizeof(crc_val)); + + bytes_to_copy += 4; + size += 4; + } + + /* Pad to minimum length */ + if (size < 64) { + size = 64; + } + + DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size); + + packet_desc_addr = s->rx_desc_addr; + while (1) { + DB_PRINT("read descriptor 0x%x\n", packet_desc_addr); + /* read current descriptor */ + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + + /* Descriptor owned by software ? */ + if (rx_desc_get_ownership(desc) == 1) { + DB_PRINT("descriptor 0x%x owned by sw.\n", packet_desc_addr); + s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; + /* Handle interrupt consequences */ + gem_update_int_status(s); + return -1; + } + + DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize), + rx_desc_get_buffer(desc)); + + /* + * Let's have QEMU lend a helping hand. + */ + if (rx_desc_get_buffer(desc) == 0) { + DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n", + packet_desc_addr); + break; + } + + /* Copy packet data to emulated DMA buffer */ + cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset, + rxbuf_ptr, MIN(bytes_to_copy, rxbufsize)); + bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); + rxbuf_ptr += MIN(bytes_to_copy, rxbufsize); + if (bytes_to_copy == 0) { + break; + } + + /* Next descriptor */ + if (rx_desc_get_wrap(desc)) { + packet_desc_addr = s->regs[GEM_RXQBASE]; + } else { + packet_desc_addr += 8; + } + } + + DB_PRINT("set length: %ld, EOF on descriptor 0x%x\n", size, + (unsigned)packet_desc_addr); + + /* Update last descriptor with EOF and total length */ + rx_desc_set_eof(desc); + rx_desc_set_length(desc, size); + cpu_physical_memory_write(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + + /* Advance RX packet descriptor Q */ + last_desc_addr = packet_desc_addr; + packet_desc_addr = s->rx_desc_addr; + s->rx_desc_addr = last_desc_addr; + if (rx_desc_get_wrap(desc)) { + s->rx_desc_addr = s->regs[GEM_RXQBASE]; + } else { + s->rx_desc_addr += 8; + } + + DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", packet_desc_addr); + + /* Count it */ + gem_receive_updatestats(s, buf, size); + + /* Update first descriptor (which could also be the last) */ + /* read descriptor */ + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + rx_desc_set_sof(desc); + rx_desc_set_ownership(desc); + cpu_physical_memory_write(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + + s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; + + /* Handle interrupt consequences */ + gem_update_int_status(s); + + return size; +} + +/* + * gem_transmit_updatestats: + * Increment transmit statistics. + */ +static void gem_transmit_updatestats(GemState *s, const uint8_t *packet, + unsigned bytes) +{ + uint64_t octets; + + /* Total octets (bytes) transmitted */ + octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) | + s->regs[GEM_OCTTXHI]; + octets += bytes; + s->regs[GEM_OCTTXLO] = octets >> 32; + s->regs[GEM_OCTTXHI] = octets; + + /* Error-free Frames transmitted */ + s->regs[GEM_TXCNT]++; + + /* Error-free Broadcast Frames counter */ + if (!memcmp(packet, broadcast_addr, 6)) { + s->regs[GEM_TXBCNT]++; + } + + /* Error-free Multicast Frames counter */ + if (packet[0] == 0x01) { + s->regs[GEM_TXMCNT]++; + } + + if (bytes <= 64) { + s->regs[GEM_TX64CNT]++; + } else if (bytes <= 127) { + s->regs[GEM_TX65CNT]++; + } else if (bytes <= 255) { + s->regs[GEM_TX128CNT]++; + } else if (bytes <= 511) { + s->regs[GEM_TX256CNT]++; + } else if (bytes <= 1023) { + s->regs[GEM_TX512CNT]++; + } else if (bytes <= 1518) { + s->regs[GEM_TX1024CNT]++; + } else { + s->regs[GEM_TX1519CNT]++; + } +} + +/* + * gem_transmit: + * Fish packets out of the descriptor ring and feed them to QEMU + */ +static void gem_transmit(GemState *s) +{ + unsigned desc[2]; + target_phys_addr_t packet_desc_addr; + uint8_t tx_packet[2048]; + uint8_t *p; + unsigned total_bytes; + + /* Do nothing if transmit is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + return; + } + + DB_PRINT("\n"); + + /* The packet we will hand off to qemu. + * Packets scattered across multiple descriptors are gathered to this + * one contiguous buffer first. + */ + p = tx_packet; + total_bytes = 0; + + /* read current descriptor */ + packet_desc_addr = s->tx_desc_addr; + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + /* Handle all descriptors owned by hardware */ + while (tx_desc_get_used(desc) == 0) { + + /* Do nothing if transmit is not enabled. */ + if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + return; + } + print_gem_tx_desc(desc); + + /* The real hardware would eat this (and possibly crash). + * For QEMU let's lend a helping hand. + */ + if ((tx_desc_get_buffer(desc) == 0) || + (tx_desc_get_length(desc) == 0)) { + DB_PRINT("Invalid TX descriptor @ 0x%x\n", packet_desc_addr); + break; + } + + /* Gather this fragment of the packet from "dma memory" to our contig. + * buffer. + */ + cpu_physical_memory_read(tx_desc_get_buffer(desc), p, + tx_desc_get_length(desc)); + p += tx_desc_get_length(desc); + total_bytes += tx_desc_get_length(desc); + + /* Last descriptor for this packet; hand the whole thing off */ + if (tx_desc_get_last(desc)) { + /* Modify the 1st descriptor of this packet to be owned by + * the processor. + */ + cpu_physical_memory_read(s->tx_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + tx_desc_set_used(desc); + cpu_physical_memory_write(s->tx_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + /* Advance the hardare current descriptor past this packet */ + if (tx_desc_get_wrap(desc)) { + s->tx_desc_addr = s->regs[GEM_TXQBASE]; + } else { + s->tx_desc_addr = packet_desc_addr + 8; + } + DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr); + + s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; + + /* Handle interrupt consequences */ + gem_update_int_status(s); + + /* Is checksum offload enabled? */ + if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) { + net_checksum_calculate(tx_packet, total_bytes); + } + + /* Update MAC statistics */ + gem_transmit_updatestats(s, tx_packet, total_bytes); + + /* Send the packet somewhere */ + if (s->phy_loop) { + gem_receive(&s->nic->nc, tx_packet, total_bytes); + } else { + qemu_send_packet(&s->nic->nc, tx_packet, total_bytes); + } + + /* Prepare for next packet */ + p = tx_packet; + total_bytes = 0; + } + + /* read next descriptor */ + if (tx_desc_get_wrap(desc)) { + packet_desc_addr = s->regs[GEM_TXQBASE]; + } else { + packet_desc_addr += 8; + } + cpu_physical_memory_read(packet_desc_addr, + (uint8_t *)&desc[0], sizeof(desc)); + } + + if (tx_desc_get_used(desc)) { + s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED; + gem_update_int_status(s); + } +} + +static void gem_phy_reset(GemState *s) +{ + memset(&s->phy_regs[0], 0, sizeof(s->phy_regs)); + s->phy_regs[PHY_REG_CONTROL] = 0x1140; + s->phy_regs[PHY_REG_STATUS] = 0x7969; + s->phy_regs[PHY_REG_PHYID1] = 0x0141; + s->phy_regs[PHY_REG_PHYID2] = 0x0CC2; + s->phy_regs[PHY_REG_ANEGADV] = 0x01E1; + s->phy_regs[PHY_REG_LINKPABIL] = 0xCDE1; + s->phy_regs[PHY_REG_ANEGEXP] = 0x000F; + s->phy_regs[PHY_REG_NEXTP] = 0x2001; + s->phy_regs[PHY_REG_LINKPNEXTP] = 0x40E6; + s->phy_regs[PHY_REG_100BTCTRL] = 0x0300; + s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00; + s->phy_regs[PHY_REG_EXTSTAT] = 0x3000; + s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078; + s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00; + s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60; + s->phy_regs[PHY_REG_LED] = 0x4100; + s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A; + s->phy_regs[PHY_REG_EXT_PHYSPCFC_ST] = 0x848B; + + phy_update_link(s); +} + +static void gem_reset(DeviceState *d) +{ + GemState *s = FROM_SYSBUS(GemState, sysbus_from_qdev(d)); + + DB_PRINT("\n"); + + /* Set post reset register values */ + memset(&s->regs[0], 0, sizeof(s->regs)); + s->regs[GEM_NWCFG] = 0x00080000; + s->regs[GEM_NWSTATUS] = 0x00000006; + s->regs[GEM_DMACFG] = 0x00020784; + s->regs[GEM_IMR] = 0x07ffffff; + s->regs[GEM_TXPAUSE] = 0x0000ffff; + s->regs[GEM_TXPARTIALSF] = 0x000003ff; + s->regs[GEM_RXPARTIALSF] = 0x000003ff; + s->regs[GEM_MODID] = 0x00020118; + s->regs[GEM_DESCONF] = 0x02500111; + s->regs[GEM_DESCONF2] = 0x2ab13fff; + s->regs[GEM_DESCONF5] = 0x002f2145; + s->regs[GEM_DESCONF6] = 0x00000200; + + gem_phy_reset(s); + + gem_update_int_status(s); +} + +static uint16_t gem_phy_read(GemState *s, unsigned reg_num) +{ + DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]); + return s->phy_regs[reg_num]; +} + +static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val) +{ + DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val); + + switch (reg_num) { + case PHY_REG_CONTROL: + if (val & PHY_REG_CONTROL_RST) { + /* Phy reset */ + gem_phy_reset(s); + val &= ~(PHY_REG_CONTROL_RST | PHY_REG_CONTROL_LOOP); + s->phy_loop = 0; + } + if (val & PHY_REG_CONTROL_ANEG) { + /* Complete autonegotiation immediately */ + val &= ~PHY_REG_CONTROL_ANEG; + s->phy_regs[PHY_REG_STATUS] |= PHY_REG_STATUS_ANEGCMPL; + } + if (val & PHY_REG_CONTROL_LOOP) { + DB_PRINT("PHY placed in loopback\n"); + s->phy_loop = 1; + } else { + s->phy_loop = 0; + } + break; + } + s->phy_regs[reg_num] = val; +} + +/* + * gem_read32: + * Read a GEM register. + */ +static uint64_t gem_read(void *opaque, target_phys_addr_t offset, unsigned size) +{ + GemState *s; + uint32_t retval; + + s = (GemState *)opaque; + + offset >>= 2; + retval = s->regs[offset]; + + DB_PRINT("offset: 0x%04x read: 0x%08x\n", offset*4, retval); + + switch (offset) { + case GEM_ISR: + qemu_set_irq(s->irq, 0); + break; + case GEM_PHYMNTNC: + if (retval & GEM_PHYMNTNC_OP_R) { + uint32_t phy_addr, reg_num; + + phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; + if (phy_addr == BOARD_PHY_ADDRESS) { + reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; + retval &= 0xFFFF0000; + retval |= gem_phy_read(s, reg_num); + } else { + retval |= 0xFFFF; /* No device at this address */ + } + } + break; + } + + /* Squash read to clear bits */ + s->regs[offset] &= ~(s->regs_rtc[offset]); + + /* Do not provide write only bits */ + retval &= ~(s->regs_wo[offset]); + + DB_PRINT("0x%08x\n", retval); + return retval; +} + +/* + * gem_write32: + * Write a GEM register. + */ +static void gem_write(void *opaque, target_phys_addr_t offset, uint64_t val, + unsigned size) +{ + GemState *s = (GemState *)opaque; + uint32_t readonly; + + DB_PRINT("offset: 0x%04x write: 0x%08x ", offset, (unsigned)val); + offset >>= 2; + + /* Squash bits which are read only in write value */ + val &= ~(s->regs_ro[offset]); + /* Preserve (only) bits which are read only in register */ + readonly = s->regs[offset]; + readonly &= s->regs_ro[offset]; + + /* Squash bits which are write 1 to clear */ + val &= ~(s->regs_w1c[offset] & val); + + /* Copy register write to backing store */ + s->regs[offset] = val | readonly; + + /* Handle register write side effects */ + switch (offset) { + case GEM_NWCTRL: + if (val & GEM_NWCTRL_TXSTART) { + gem_transmit(s); + } + if (!(val & GEM_NWCTRL_TXENA)) { + /* Reset to start of Q when transmit disabled. */ + s->tx_desc_addr = s->regs[GEM_TXQBASE]; + } + if (!(val & GEM_NWCTRL_RXENA)) { + /* Reset to start of Q when receive disabled. */ + s->rx_desc_addr = s->regs[GEM_RXQBASE]; + } + break; + + case GEM_TXSTATUS: + gem_update_int_status(s); + break; + case GEM_RXQBASE: + s->rx_desc_addr = val; + break; + case GEM_TXQBASE: + s->tx_desc_addr = val; + break; + case GEM_RXSTATUS: + gem_update_int_status(s); + break; + case GEM_IER: + s->regs[GEM_IMR] &= ~val; + gem_update_int_status(s); + break; + case GEM_IDR: + s->regs[GEM_IMR] |= val; + gem_update_int_status(s); + break; + case GEM_PHYMNTNC: + if (val & GEM_PHYMNTNC_OP_W) { + uint32_t phy_addr, reg_num; + + phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; + if (phy_addr == BOARD_PHY_ADDRESS) { + reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; + gem_phy_write(s, reg_num, val); + } + } + break; + } + + DB_PRINT("newval: 0x%08x\n", s->regs[offset]); +} + +static const MemoryRegionOps gem_ops = { + .read = gem_read, + .write = gem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void gem_cleanup(VLANClientState *nc) +{ + GemState *s = DO_UPCAST(NICState, nc, nc)->opaque; + + DB_PRINT("\n"); + s->nic = NULL; +} + +static void gem_set_link(VLANClientState *nc) +{ + DB_PRINT("\n"); + phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque); +} + +static NetClientInfo net_gem_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = gem_can_receive, + .receive = gem_receive, + .cleanup = gem_cleanup, + .link_status_changed = gem_set_link, +}; + +static int gem_init(SysBusDevice *dev) +{ + GemState *s; + + DB_PRINT("\n"); + + s = FROM_SYSBUS(GemState, dev); + gem_init_register_masks(s); + memory_region_init_io(&s->iomem, &gem_ops, s, "enet", sizeof(s->regs)); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->nic = qemu_new_nic(&net_gem_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->qdev.id, s); + + return 0; +} + +static const VMStateDescription vmstate_cadence_gem = { + .name = "cadence_gem", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG), + VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32), + VMSTATE_UINT8(phy_loop, GemState), + VMSTATE_UINT32(rx_desc_addr, GemState), + VMSTATE_UINT32(tx_desc_addr, GemState), + } +}; + +static Property gem_properties[] = { + DEFINE_NIC_PROPERTIES(GemState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void gem_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = gem_init; + dc->props = gem_properties; + dc->vmsd = &vmstate_cadence_gem; + dc->reset = gem_reset; +} + +static TypeInfo gem_info = { + .class_init = gem_class_init, + .name = "cadence_gem", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GemState), +}; + +static void gem_register_types(void) +{ + type_register_static(&gem_info); +} + +type_init(gem_register_types) diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c new file mode 100644 index 0000000000..2b5477b688 --- /dev/null +++ b/hw/cadence_ttc.c @@ -0,0 +1,489 @@ +/* + * Xilinx Zynq cadence TTC model + * + * Copyright (c) 2011 Xilinx Inc. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) + * Copyright (c) 2012 PetaLogix Pty Ltd. + * Written By Haibing Ma + * M. Habib + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "qemu-timer.h" + +#ifdef CADENCE_TTC_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define COUNTER_INTR_IV 0x00000001 +#define COUNTER_INTR_M1 0x00000002 +#define COUNTER_INTR_M2 0x00000004 +#define COUNTER_INTR_M3 0x00000008 +#define COUNTER_INTR_OV 0x00000010 +#define COUNTER_INTR_EV 0x00000020 + +#define COUNTER_CTRL_DIS 0x00000001 +#define COUNTER_CTRL_INT 0x00000002 +#define COUNTER_CTRL_DEC 0x00000004 +#define COUNTER_CTRL_MATCH 0x00000008 +#define COUNTER_CTRL_RST 0x00000010 + +#define CLOCK_CTRL_PS_EN 0x00000001 +#define CLOCK_CTRL_PS_V 0x0000001e + +typedef struct { + QEMUTimer *timer; + int freq; + + uint32_t reg_clock; + uint32_t reg_count; + uint32_t reg_value; + uint16_t reg_interval; + uint16_t reg_match[3]; + uint32_t reg_intr; + uint32_t reg_intr_en; + uint32_t reg_event_ctrl; + uint32_t reg_event; + + uint64_t cpu_time; + unsigned int cpu_time_valid; + + qemu_irq irq; +} CadenceTimerState; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + CadenceTimerState timer[3]; +} CadenceTTCState; + +static void cadence_timer_update(CadenceTimerState *s) +{ + qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en)); +} + +static CadenceTimerState *cadence_timer_from_addr(void *opaque, + target_phys_addr_t offset) +{ + unsigned int index; + CadenceTTCState *s = (CadenceTTCState *)opaque; + + index = (offset >> 2) % 3; + + return &s->timer[index]; +} + +static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps) +{ + /* timer_steps has max value of 0x100000000. double check it + * (or overflow can happen below) */ + assert(timer_steps <= 1ULL << 32); + + uint64_t r = timer_steps * 1000000000ULL; + if (s->reg_clock & CLOCK_CTRL_PS_EN) { + r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); + } else { + r >>= 16; + } + r /= (uint64_t)s->freq; + return r; +} + +static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns) +{ + uint64_t to_divide = 1000000000ULL; + + uint64_t r = ns; + /* for very large intervals (> 8s) do some division first to stop + * overflow (costs some prescision) */ + while (r >= 8ULL << 30 && to_divide > 1) { + r /= 1000; + to_divide /= 1000; + } + r <<= 16; + /* keep early-dividing as needed */ + while (r >= 8ULL << 30 && to_divide > 1) { + r /= 1000; + to_divide /= 1000; + } + r *= (uint64_t)s->freq; + if (s->reg_clock & CLOCK_CTRL_PS_EN) { + r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); + } + + r /= to_divide; + return r; +} + +/* determine if x is in between a and b, exclusive of a, inclusive of b */ + +static inline int64_t is_between(int64_t x, int64_t a, int64_t b) +{ + if (a < b) { + return x > a && x <= b; + } + return x < a && x >= b; +} + +static void cadence_timer_run(CadenceTimerState *s) +{ + int i; + int64_t event_interval, next_value; + + assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */ + + if (s->reg_count & COUNTER_CTRL_DIS) { + s->cpu_time_valid = 0; + return; + } + + { /* figure out what's going to happen next (rollover or match) */ + int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ? + (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; + next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval; + for (i = 0; i < 3; ++i) { + int64_t cand = (uint64_t)s->reg_match[i] << 16; + if (is_between(cand, (uint64_t)s->reg_value, next_value)) { + next_value = cand; + } + } + } + DB_PRINT("next timer event value: %09llx\n", + (unsigned long long)next_value); + + event_interval = next_value - (int64_t)s->reg_value; + event_interval = (event_interval < 0) ? -event_interval : event_interval; + + qemu_mod_timer(s->timer, s->cpu_time + + cadence_timer_get_ns(s, event_interval)); +} + +static void cadence_timer_sync(CadenceTimerState *s) +{ + int i; + int64_t r, x; + int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ? + (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; + uint64_t old_time = s->cpu_time; + + s->cpu_time = qemu_get_clock_ns(vm_clock); + DB_PRINT("cpu time: %lld ns\n", (long long)old_time); + + if (!s->cpu_time_valid || old_time == s->cpu_time) { + s->cpu_time_valid = 1; + return; + } + + r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time); + x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r); + + for (i = 0; i < 3; ++i) { + int64_t m = (int64_t)s->reg_match[i] << 16; + if (m > interval) { + continue; + } + /* check to see if match event has occurred. check m +/- interval + * to account for match events in wrap around cases */ + if (is_between(m, s->reg_value, x) || + is_between(m + interval, s->reg_value, x) || + is_between(m - interval, s->reg_value, x)) { + s->reg_intr |= (2 << i); + } + } + while (x < 0) { + x += interval; + } + s->reg_value = (uint32_t)(x % interval); + + if (s->reg_value != x) { + s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? + COUNTER_INTR_IV : COUNTER_INTR_OV; + } + cadence_timer_update(s); +} + +static void cadence_timer_tick(void *opaque) +{ + CadenceTimerState *s = opaque; + + DB_PRINT("\n"); + cadence_timer_sync(s); + cadence_timer_run(s); +} + +static uint32_t cadence_ttc_read_imp(void *opaque, target_phys_addr_t offset) +{ + CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); + uint32_t value; + + cadence_timer_sync(s); + cadence_timer_run(s); + + switch (offset) { + case 0x00: /* clock control */ + case 0x04: + case 0x08: + return s->reg_clock; + + case 0x0c: /* counter control */ + case 0x10: + case 0x14: + return s->reg_count; + + case 0x18: /* counter value */ + case 0x1c: + case 0x20: + return (uint16_t)(s->reg_value >> 16); + + case 0x24: /* reg_interval counter */ + case 0x28: + case 0x2c: + return s->reg_interval; + + case 0x30: /* match 1 counter */ + case 0x34: + case 0x38: + return s->reg_match[0]; + + case 0x3c: /* match 2 counter */ + case 0x40: + case 0x44: + return s->reg_match[1]; + + case 0x48: /* match 3 counter */ + case 0x4c: + case 0x50: + return s->reg_match[2]; + + case 0x54: /* interrupt register */ + case 0x58: + case 0x5c: + /* cleared after read */ + value = s->reg_intr; + s->reg_intr = 0; + return value; + + case 0x60: /* interrupt enable */ + case 0x64: + case 0x68: + return s->reg_intr_en; + + case 0x6c: + case 0x70: + case 0x74: + return s->reg_event_ctrl; + + case 0x78: + case 0x7c: + case 0x80: + return s->reg_event; + + default: + return 0; + } +} + +static uint64_t cadence_ttc_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + uint32_t ret = cadence_ttc_read_imp(opaque, offset); + + DB_PRINT("addr: %08x data: %08x\n", offset, ret); + return ret; +} + +static void cadence_ttc_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); + + DB_PRINT("addr: %08x data %08x\n", offset, (unsigned)value); + + cadence_timer_sync(s); + + switch (offset) { + case 0x00: /* clock control */ + case 0x04: + case 0x08: + s->reg_clock = value & 0x3F; + break; + + case 0x0c: /* counter control */ + case 0x10: + case 0x14: + if (value & COUNTER_CTRL_RST) { + s->reg_value = 0; + } + s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST; + break; + + case 0x24: /* interval register */ + case 0x28: + case 0x2c: + s->reg_interval = value & 0xffff; + break; + + case 0x30: /* match register */ + case 0x34: + case 0x38: + s->reg_match[0] = value & 0xffff; + + case 0x3c: /* match register */ + case 0x40: + case 0x44: + s->reg_match[1] = value & 0xffff; + + case 0x48: /* match register */ + case 0x4c: + case 0x50: + s->reg_match[2] = value & 0xffff; + break; + + case 0x54: /* interrupt register */ + case 0x58: + case 0x5c: + s->reg_intr &= (~value & 0xfff); + break; + + case 0x60: /* interrupt enable */ + case 0x64: + case 0x68: + s->reg_intr_en = value & 0x3f; + break; + + case 0x6c: /* event control */ + case 0x70: + case 0x74: + s->reg_event_ctrl = value & 0x07; + break; + + default: + return; + } + + cadence_timer_run(s); + cadence_timer_update(s); +} + +static const MemoryRegionOps cadence_ttc_ops = { + .read = cadence_ttc_read, + .write = cadence_ttc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void cadence_timer_reset(CadenceTimerState *s) +{ + s->reg_count = 0x21; +} + +static void cadence_timer_init(uint32_t freq, CadenceTimerState *s) +{ + memset(s, 0, sizeof(CadenceTimerState)); + s->freq = freq; + + cadence_timer_reset(s); + + s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s); +} + +static int cadence_ttc_init(SysBusDevice *dev) +{ + CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev); + int i; + + for (i = 0; i < 3; ++i) { + cadence_timer_init(2500000, &s->timer[i]); + sysbus_init_irq(dev, &s->timer[i].irq); + } + + memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void cadence_timer_pre_save(void *opaque) +{ + cadence_timer_sync((CadenceTimerState *)opaque); +} + +static int cadence_timer_post_load(void *opaque, int version_id) +{ + CadenceTimerState *s = opaque; + + s->cpu_time_valid = 0; + cadence_timer_sync(s); + cadence_timer_run(s); + cadence_timer_update(s); + return 0; +} + +static const VMStateDescription vmstate_cadence_timer = { + .name = "cadence_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = cadence_timer_pre_save, + .post_load = cadence_timer_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(reg_clock, CadenceTimerState), + VMSTATE_UINT32(reg_count, CadenceTimerState), + VMSTATE_UINT32(reg_value, CadenceTimerState), + VMSTATE_UINT16(reg_interval, CadenceTimerState), + VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3), + VMSTATE_UINT32(reg_intr, CadenceTimerState), + VMSTATE_UINT32(reg_intr_en, CadenceTimerState), + VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState), + VMSTATE_UINT32(reg_event, CadenceTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_cadence_ttc = { + .name = "cadence_TTC", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0, + vmstate_cadence_timer, + CadenceTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void cadence_ttc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = cadence_ttc_init; + dc->vmsd = &vmstate_cadence_ttc; +} + +static TypeInfo cadence_ttc_info = { + .name = "cadence_ttc", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CadenceTTCState), + .class_init = cadence_ttc_class_init, +}; + +static void cadence_ttc_register_types(void) +{ + type_register_static(&cadence_ttc_info); +} + +type_init(cadence_ttc_register_types) diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c new file mode 100644 index 0000000000..d98e531372 --- /dev/null +++ b/hw/cadence_uart.c @@ -0,0 +1,513 @@ +/* + * Device model for Cadence UART + * + * Copyright (c) 2010 Xilinx Inc. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) + * Copyright (c) 2012 PetaLogix Pty Ltd. + * Written by Haibing Ma + * M.Habib + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "qemu-char.h" +#include "qemu-timer.h" + +#ifdef CADENCE_UART_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define UART_SR_INTR_RTRIG 0x00000001 +#define UART_SR_INTR_REMPTY 0x00000002 +#define UART_SR_INTR_RFUL 0x00000004 +#define UART_SR_INTR_TEMPTY 0x00000008 +#define UART_SR_INTR_TFUL 0x00000010 +/* bits fields in CSR that correlate to CISR. If any of these bits are set in + * SR, then the same bit in CISR is set high too */ +#define UART_SR_TO_CISR_MASK 0x0000001F + +#define UART_INTR_ROVR 0x00000020 +#define UART_INTR_FRAME 0x00000040 +#define UART_INTR_PARE 0x00000080 +#define UART_INTR_TIMEOUT 0x00000100 +#define UART_INTR_DMSI 0x00000200 + +#define UART_SR_RACTIVE 0x00000400 +#define UART_SR_TACTIVE 0x00000800 +#define UART_SR_FDELT 0x00001000 + +#define UART_CR_RXRST 0x00000001 +#define UART_CR_TXRST 0x00000002 +#define UART_CR_RX_EN 0x00000004 +#define UART_CR_RX_DIS 0x00000008 +#define UART_CR_TX_EN 0x00000010 +#define UART_CR_TX_DIS 0x00000020 +#define UART_CR_RST_TO 0x00000040 +#define UART_CR_STARTBRK 0x00000080 +#define UART_CR_STOPBRK 0x00000100 + +#define UART_MR_CLKS 0x00000001 +#define UART_MR_CHRL 0x00000006 +#define UART_MR_CHRL_SH 1 +#define UART_MR_PAR 0x00000038 +#define UART_MR_PAR_SH 3 +#define UART_MR_NBSTOP 0x000000C0 +#define UART_MR_NBSTOP_SH 6 +#define UART_MR_CHMODE 0x00000300 +#define UART_MR_CHMODE_SH 8 +#define UART_MR_UCLKEN 0x00000400 +#define UART_MR_IRMODE 0x00000800 + +#define UART_DATA_BITS_6 (0x3 << UART_MR_CHRL_SH) +#define UART_DATA_BITS_7 (0x2 << UART_MR_CHRL_SH) +#define UART_PARITY_ODD (0x1 << UART_MR_PAR_SH) +#define UART_PARITY_EVEN (0x0 << UART_MR_PAR_SH) +#define UART_STOP_BITS_1 (0x3 << UART_MR_NBSTOP_SH) +#define UART_STOP_BITS_2 (0x2 << UART_MR_NBSTOP_SH) +#define NORMAL_MODE (0x0 << UART_MR_CHMODE_SH) +#define ECHO_MODE (0x1 << UART_MR_CHMODE_SH) +#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH) +#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH) + +#define RX_FIFO_SIZE 16 +#define TX_FIFO_SIZE 16 +#define UART_INPUT_CLK 50000000 + +#define R_CR (0x00/4) +#define R_MR (0x04/4) +#define R_IER (0x08/4) +#define R_IDR (0x0C/4) +#define R_IMR (0x10/4) +#define R_CISR (0x14/4) +#define R_BRGR (0x18/4) +#define R_RTOR (0x1C/4) +#define R_RTRIG (0x20/4) +#define R_MCR (0x24/4) +#define R_MSR (0x28/4) +#define R_SR (0x2C/4) +#define R_TX_RX (0x30/4) +#define R_BDIV (0x34/4) +#define R_FDEL (0x38/4) +#define R_PMIN (0x3C/4) +#define R_PWID (0x40/4) +#define R_TTRIG (0x44/4) + +#define R_MAX (R_TTRIG + 1) + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + uint32_t r[R_MAX]; + uint8_t r_fifo[RX_FIFO_SIZE]; + uint32_t rx_wpos; + uint32_t rx_count; + uint64_t char_tx_time; + CharDriverState *chr; + qemu_irq irq; + struct QEMUTimer *fifo_trigger_handle; + struct QEMUTimer *tx_time_handle; +} UartState; + +static void uart_update_status(UartState *s) +{ + s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK; + qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR])); +} + +static void fifo_trigger_update(void *opaque) +{ + UartState *s = (UartState *)opaque; + + s->r[R_CISR] |= UART_INTR_TIMEOUT; + + uart_update_status(s); +} + +static void uart_tx_redo(UartState *s) +{ + uint64_t new_tx_time = qemu_get_clock_ns(vm_clock); + + qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time); + + s->r[R_SR] |= UART_SR_INTR_TEMPTY; + + uart_update_status(s); +} + +static void uart_tx_write(void *opaque) +{ + UartState *s = (UartState *)opaque; + + uart_tx_redo(s); +} + +static void uart_rx_reset(UartState *s) +{ + s->rx_wpos = 0; + s->rx_count = 0; + + s->r[R_SR] |= UART_SR_INTR_REMPTY; + s->r[R_SR] &= ~UART_SR_INTR_RFUL; +} + +static void uart_tx_reset(UartState *s) +{ + s->r[R_SR] |= UART_SR_INTR_TEMPTY; + s->r[R_SR] &= ~UART_SR_INTR_TFUL; +} + +static void uart_send_breaks(UartState *s) +{ + int break_enabled = 1; + + qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &break_enabled); +} + +static void uart_parameters_setup(UartState *s) +{ + QEMUSerialSetParams ssp; + unsigned int baud_rate, packet_size; + + baud_rate = (s->r[R_MR] & UART_MR_CLKS) ? + UART_INPUT_CLK / 8 : UART_INPUT_CLK; + + ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1)); + packet_size = 1; + + switch (s->r[R_MR] & UART_MR_PAR) { + case UART_PARITY_EVEN: + ssp.parity = 'E'; + packet_size++; + break; + case UART_PARITY_ODD: + ssp.parity = 'O'; + packet_size++; + break; + default: + ssp.parity = 'N'; + break; + } + + switch (s->r[R_MR] & UART_MR_CHRL) { + case UART_DATA_BITS_6: + ssp.data_bits = 6; + break; + case UART_DATA_BITS_7: + ssp.data_bits = 7; + break; + default: + ssp.data_bits = 8; + break; + } + + switch (s->r[R_MR] & UART_MR_NBSTOP) { + case UART_STOP_BITS_1: + ssp.stop_bits = 1; + break; + default: + ssp.stop_bits = 2; + break; + } + + packet_size += ssp.data_bits + ssp.stop_bits; + s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size; + qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); +} + +static int uart_can_receive(void *opaque) +{ + UartState *s = (UartState *)opaque; + + return RX_FIFO_SIZE - s->rx_count; +} + +static void uart_ctrl_update(UartState *s) +{ + if (s->r[R_CR] & UART_CR_TXRST) { + uart_tx_reset(s); + } + + if (s->r[R_CR] & UART_CR_RXRST) { + uart_rx_reset(s); + } + + s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST); + + if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) { + uart_tx_redo(s); + } + + if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) { + uart_send_breaks(s); + } +} + +static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size) +{ + UartState *s = (UartState *)opaque; + uint64_t new_rx_time = qemu_get_clock_ns(vm_clock); + int i; + + if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) { + return; + } + + s->r[R_SR] &= ~UART_SR_INTR_REMPTY; + + if (s->rx_count == RX_FIFO_SIZE) { + s->r[R_CISR] |= UART_INTR_ROVR; + } else { + for (i = 0; i < size; i++) { + s->r_fifo[s->rx_wpos] = buf[i]; + s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE; + s->rx_count++; + + if (s->rx_count == RX_FIFO_SIZE) { + s->r[R_SR] |= UART_SR_INTR_RFUL; + break; + } + + if (s->rx_count >= s->r[R_RTRIG]) { + s->r[R_SR] |= UART_SR_INTR_RTRIG; + } + } + qemu_mod_timer(s->fifo_trigger_handle, new_rx_time + + (s->char_tx_time * 4)); + } + uart_update_status(s); +} + +static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size) +{ + if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) { + return; + } + + while (size) { + size -= qemu_chr_fe_write(s->chr, buf, size); + } +} + +static void uart_receive(void *opaque, const uint8_t *buf, int size) +{ + UartState *s = (UartState *)opaque; + uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE; + + if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) { + uart_write_rx_fifo(opaque, buf, size); + } + if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) { + uart_write_tx_fifo(s, buf, size); + } +} + +static void uart_event(void *opaque, int event) +{ + UartState *s = (UartState *)opaque; + uint8_t buf = '\0'; + + if (event == CHR_EVENT_BREAK) { + uart_write_rx_fifo(opaque, &buf, 1); + } + + uart_update_status(s); +} + +static void uart_read_rx_fifo(UartState *s, uint32_t *c) +{ + if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) { + return; + } + + s->r[R_SR] &= ~UART_SR_INTR_RFUL; + + if (s->rx_count) { + uint32_t rx_rpos = + (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE; + *c = s->r_fifo[rx_rpos]; + s->rx_count--; + + if (!s->rx_count) { + s->r[R_SR] |= UART_SR_INTR_REMPTY; + } + } else { + *c = 0; + s->r[R_SR] |= UART_SR_INTR_REMPTY; + } + + if (s->rx_count < s->r[R_RTRIG]) { + s->r[R_SR] &= ~UART_SR_INTR_RTRIG; + } + uart_update_status(s); +} + +static void uart_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + UartState *s = (UartState *)opaque; + + DB_PRINT(" offset:%x data:%08x\n", offset, (unsigned)value); + offset >>= 2; + switch (offset) { + case R_IER: /* ier (wts imr) */ + s->r[R_IMR] |= value; + break; + case R_IDR: /* idr (wtc imr) */ + s->r[R_IMR] &= ~value; + break; + case R_IMR: /* imr (read only) */ + break; + case R_CISR: /* cisr (wtc) */ + s->r[R_CISR] &= ~value; + break; + case R_TX_RX: /* UARTDR */ + switch (s->r[R_MR] & UART_MR_CHMODE) { + case NORMAL_MODE: + uart_write_tx_fifo(s, (uint8_t *) &value, 1); + break; + case LOCAL_LOOPBACK: + uart_write_rx_fifo(opaque, (uint8_t *) &value, 1); + break; + } + break; + default: + s->r[offset] = value; + } + + switch (offset) { + case R_CR: + uart_ctrl_update(s); + break; + case R_MR: + uart_parameters_setup(s); + break; + } +} + +static uint64_t uart_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + UartState *s = (UartState *)opaque; + uint32_t c = 0; + + offset >>= 2; + if (offset > R_MAX) { + return 0; + } else if (offset == R_TX_RX) { + uart_read_rx_fifo(s, &c); + return c; + } + return s->r[offset]; +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void cadence_uart_reset(UartState *s) +{ + s->r[R_CR] = 0x00000128; + s->r[R_IMR] = 0; + s->r[R_CISR] = 0; + s->r[R_RTRIG] = 0x00000020; + s->r[R_BRGR] = 0x0000000F; + s->r[R_TTRIG] = 0x00000020; + + uart_rx_reset(s); + uart_tx_reset(s); + + s->rx_count = 0; + s->rx_wpos = 0; +} + +static int cadence_uart_init(SysBusDevice *dev) +{ + UartState *s = FROM_SYSBUS(UartState, dev); + + memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + + s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock, + (QEMUTimerCB *)fifo_trigger_update, s); + + s->tx_time_handle = qemu_new_timer_ns(vm_clock, + (QEMUTimerCB *)uart_tx_write, s); + + s->char_tx_time = (get_ticks_per_sec() / 9600) * 10; + + s->chr = qemu_char_get_next_serial(); + + cadence_uart_reset(s); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive, + uart_event, s); + } + + return 0; +} + +static int cadence_uart_post_load(void *opaque, int version_id) +{ + UartState *s = opaque; + + uart_parameters_setup(s); + uart_update_status(s); + return 0; +} + +static const VMStateDescription vmstate_cadence_uart = { + .name = "cadence_uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = cadence_uart_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(r, UartState, R_MAX), + VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE), + VMSTATE_UINT32(rx_count, UartState), + VMSTATE_UINT32(rx_wpos, UartState), + VMSTATE_TIMER(fifo_trigger_handle, UartState), + VMSTATE_TIMER(tx_time_handle, UartState), + VMSTATE_END_OF_LIST() + } +}; + +static void cadence_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = cadence_uart_init; + dc->vmsd = &vmstate_cadence_uart; +} + +static TypeInfo cadence_uart_info = { + .name = "cadence_uart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UartState), + .class_init = cadence_uart_class_init, +}; + +static void cadence_uart_register_types(void) +{ + type_register_static(&cadence_uart_info); +} + +type_init(cadence_uart_register_types) diff --git a/hw/cris-boot.c b/hw/cris-boot.c index 37894f8b53..ca6c52fa8e 100644 --- a/hw/cris-boot.c +++ b/hw/cris-boot.c @@ -29,12 +29,12 @@ static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUCRISState *env = opaque; struct cris_load_info *li; li = env->load_info; - cpu_reset(env); + cpu_state_reset(env); if (!li) { /* nothing more to do. */ @@ -60,7 +60,7 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return addr - 0x80000000LL; } -void cris_load_image(CPUState *env, struct cris_load_info *li) +void cris_load_image(CPUCRISState *env, struct cris_load_info *li) { uint64_t entry, high; int kcmdline_len; diff --git a/hw/cris-boot.h b/hw/cris-boot.h index e9caf8dee8..ecb9779e49 100644 --- a/hw/cris-boot.h +++ b/hw/cris-boot.h @@ -8,4 +8,4 @@ struct cris_load_info target_phys_addr_t entry; }; -void cris_load_image(CPUState *env, struct cris_load_info *li); +void cris_load_image(CPUCRISState *env, struct cris_load_info *li); diff --git a/hw/cris_pic_cpu.c b/hw/cris_pic_cpu.c index 06ae484950..3da0e86536 100644 --- a/hw/cris_pic_cpu.c +++ b/hw/cris_pic_cpu.c @@ -30,7 +30,7 @@ static void cris_pic_cpu_handler(void *opaque, int irq, int level) { - CPUState *env = (CPUState *)opaque; + CPUCRISState *env = (CPUCRISState *)opaque; int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD; if (level) @@ -39,7 +39,7 @@ static void cris_pic_cpu_handler(void *opaque, int irq, int level) cpu_reset_interrupt(env, type); } -qemu_irq *cris_pic_init_cpu(CPUState *env) +qemu_irq *cris_pic_init_cpu(CPUCRISState *env) { return qemu_allocate_irqs(cris_pic_cpu_handler, env, 2); } diff --git a/hw/cuda.c b/hw/cuda.c index 40774360df..233ab666da 100644 --- a/hw/cuda.c +++ b/hw/cuda.c @@ -634,7 +634,7 @@ static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr) return 0; } -static MemoryRegionOps cuda_ops = { +static const MemoryRegionOps cuda_ops = { .old_mmio = { .write = { cuda_writeb, diff --git a/hw/ds1338.c b/hw/ds1338.c index 6397f0aa6f..d590d9c007 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -100,6 +100,7 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data) break; case 5: s->now.tm_mon = from_bcd(data & 0x1f) - 1; + break; case 6: s->now.tm_year = from_bcd(data) + 100; break; diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c index e3c574008f..7cc7a99bfb 100644 --- a/hw/dummy_m68k.c +++ b/hw/dummy_m68k.c @@ -21,7 +21,7 @@ static void dummy_m68k_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUM68KState *env; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); int kernel_size; diff --git a/hw/etraxfs.h b/hw/etraxfs.h index 24e8fd880b..c62f94b7b7 100644 --- a/hw/etraxfs.h +++ b/hw/etraxfs.h @@ -25,7 +25,7 @@ #include "net.h" #include "etraxfs_dma.h" -qemu_irq *cris_pic_init_cpu(CPUState *env); +qemu_irq *cris_pic_init_cpu(CPUCRISState *env); /* Instantiate an ETRAXFS Ethernet MAC. */ static inline DeviceState * diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c index cecd819584..5f16b17835 100644 --- a/hw/etraxfs_ser.c +++ b/hw/etraxfs_ser.c @@ -78,7 +78,7 @@ static uint64_t ser_read(void *opaque, target_phys_addr_t addr, unsigned int size) { struct etrax_serial *s = opaque; - D(CPUState *env = s->env); + D(CPUCRISState *env = s->env); uint32_t r = 0; addr >>= 2; @@ -116,7 +116,7 @@ ser_write(void *opaque, target_phys_addr_t addr, struct etrax_serial *s = opaque; uint32_t value = val64; unsigned char ch = val64; - D(CPUState *env = s->env); + D(CPUCRISState *env = s->env); D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value)); addr >>= 2; diff --git a/hw/exynos4210.h b/hw/exynos4210.h index e7522f851a..c112e03bfb 100644 --- a/hw/exynos4210.h +++ b/hw/exynos4210.h @@ -83,7 +83,7 @@ typedef struct Exynos4210Irq { } Exynos4210Irq; typedef struct Exynos4210State { - CPUState * env[EXYNOS4210_NCPUS]; + CPUARMState * env[EXYNOS4210_NCPUS]; Exynos4210Irq irqs; qemu_irq *irq_table; diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c index 01e3fb8a3b..7474fcf802 100644 --- a/hw/exynos4210_mct.c +++ b/hw/exynos4210_mct.c @@ -888,7 +888,7 @@ static void exynos4210_ltick_event(void *opaque) static uint64_t time2[2] = {0}; #endif - /* Call tick_timer event handler, it will update it's tcntb and icntb */ + /* Call tick_timer event handler, it will update its tcntb and icntb. */ exynos4210_ltick_timer_event(&s->tick_timer); /* get tick_timer cnt */ diff --git a/hw/fdc.c b/hw/fdc.c index 38fad587cb..a0236b7295 100644 --- a/hw/fdc.c +++ b/hw/fdc.c @@ -62,12 +62,15 @@ #define FD_SECTOR_SC 2 /* Sector size code */ #define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ +typedef struct FDCtrl FDCtrl; + /* Floppy disk drive emulation */ typedef enum FDiskFlags { FDISK_DBL_SIDES = 0x01, } FDiskFlags; typedef struct FDrive { + FDCtrl *fdctrl; BlockDriverState *bs; /* Drive status */ FDriveType drive; @@ -83,6 +86,7 @@ typedef struct FDrive { uint16_t bps; /* Bytes per sector */ uint8_t ro; /* Is read-only */ uint8_t media_changed; /* Is media changed */ + uint8_t media_rate; /* Data rate of medium */ } FDrive; static void fd_init(FDrive *drv) @@ -95,16 +99,19 @@ static void fd_init(FDrive *drv) drv->max_track = 0; } +#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1) + static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect, - uint8_t last_sect) + uint8_t last_sect, uint8_t num_sides) { - return (((track * 2) + head) * last_sect) + sect - 1; + return (((track * num_sides) + head) * last_sect) + sect - 1; } /* Returns current position, in sectors, for given drive */ static int fd_sector(FDrive *drv) { - return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect); + return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect, + NUM_SIDES(drv)); } /* Seek to a new position: @@ -135,7 +142,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, drv->max_track, drv->last_sect); return 3; } - sector = fd_sector_calc(head, track, sect, drv->last_sect); + sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv)); ret = 0; if (sector != fd_sector(drv)) { #if 0 @@ -169,12 +176,13 @@ static void fd_revalidate(FDrive *drv) { int nb_heads, max_track, last_sect, ro; FDriveType drive; + FDriveRate rate; FLOPPY_DPRINTF("revalidate\n"); if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { ro = bdrv_is_read_only(drv->bs); bdrv_get_floppy_geometry_hint(drv->bs, &nb_heads, &max_track, - &last_sect, drv->drive, &drive); + &last_sect, drv->drive, &drive, &rate); if (nb_heads != 0 && max_track != 0 && last_sect != 0) { FLOPPY_DPRINTF("User defined disk (%d %d %d)", nb_heads - 1, max_track, last_sect); @@ -191,6 +199,7 @@ static void fd_revalidate(FDrive *drv) drv->last_sect = last_sect; drv->ro = ro; drv->drive = drive; + drv->media_rate = rate; } else { FLOPPY_DPRINTF("No disk in drive\n"); drv->last_sect = 0; @@ -202,13 +211,12 @@ static void fd_revalidate(FDrive *drv) /********************************************************/ /* Intel 82078 floppy disk controller emulation */ -typedef struct FDCtrl FDCtrl; - static void fdctrl_reset(FDCtrl *fdctrl, int do_irq); static void fdctrl_reset_fifo(FDCtrl *fdctrl); static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len); static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0); +static FDrive *get_cur_drv(FDCtrl *fdctrl); static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl); static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl); @@ -221,6 +229,7 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value); static uint32_t fdctrl_read_data(FDCtrl *fdctrl); static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value); static uint32_t fdctrl_read_dir(FDCtrl *fdctrl); +static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value); enum { FD_DIR_WRITE = 0, @@ -245,6 +254,7 @@ enum { FD_REG_DSR = 0x04, FD_REG_FIFO = 0x05, FD_REG_DIR = 0x07, + FD_REG_CCR = 0x07, }; enum { @@ -297,6 +307,8 @@ enum { }; enum { + FD_SR1_MA = 0x01, /* Missing address mark */ + FD_SR1_NW = 0x02, /* Not writable */ FD_SR1_EC = 0x80, /* End of cylinder */ }; @@ -413,6 +425,7 @@ struct FDCtrl { int sun4m; FDrive drives[MAX_FD]; int reset_sensei; + uint32_t check_media_rate; /* Timers state */ uint8_t timer0; uint8_t timer1; @@ -487,6 +500,9 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) case FD_REG_FIFO: fdctrl_write_data(fdctrl, value); break; + case FD_REG_CCR: + fdctrl_write_ccr(fdctrl, value); + break; default: break; } @@ -538,6 +554,24 @@ static const VMStateDescription vmstate_fdrive_media_changed = { } }; +static bool fdrive_media_rate_needed(void *opaque) +{ + FDrive *drive = opaque; + + return drive->fdctrl->check_media_rate; +} + +static const VMStateDescription vmstate_fdrive_media_rate = { + .name = "fdrive/media_rate", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(media_rate, FDrive), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_fdrive = { .name = "fdrive", .version_id = 1, @@ -554,6 +588,9 @@ static const VMStateDescription vmstate_fdrive = { .vmsd = &vmstate_fdrive_media_changed, .needed = &fdrive_media_changed_needed, } , { + .vmsd = &vmstate_fdrive_media_rate, + .needed = &fdrive_media_rate_needed, + } , { /* empty */ } } @@ -877,6 +914,23 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value) fdctrl->dsr = value; } +/* Configuration control register: 0x07 (write) */ +static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value) +{ + /* Reset mode */ + if (!(fdctrl->dor & FD_DOR_nRESET)) { + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); + return; + } + FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value); + + /* Only the rate selection bits used in AT mode, and we + * store those in the DSR. + */ + fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) | + (value & FD_DSR_DRATEMASK); +} + static int fdctrl_media_changed(FDrive *drv) { int ret; @@ -903,14 +957,9 @@ static uint32_t fdctrl_read_dir(FDCtrl *fdctrl) { uint32_t retval = 0; - if (fdctrl_media_changed(drv0(fdctrl)) - || fdctrl_media_changed(drv1(fdctrl)) -#if MAX_FD == 4 - || fdctrl_media_changed(drv2(fdctrl)) - || fdctrl_media_changed(drv3(fdctrl)) -#endif - ) + if (fdctrl_media_changed(get_cur_drv(fdctrl))) { retval |= FD_DIR_DSKCHG; + } if (retval != 0) { FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); } @@ -1019,7 +1068,8 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) ks = fdctrl->fifo[4]; FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect)); + fd_sector_calc(kh, kt, ks, cur_drv->last_sect, + NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ @@ -1049,6 +1099,19 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) break; } + /* Check the data rate. If the programmed data rate does not match + * the currently inserted medium, the operation has to fail. */ + if (fdctrl->check_media_rate && + (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { + FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n", + fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + } + /* Set the FIFO state */ fdctrl->data_dir = direction; fdctrl->data_pos = 0; @@ -1175,6 +1238,16 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, break; case FD_DIR_WRITE: /* WRITE commands */ + if (cur_drv->ro) { + /* Handle readonly medium early, no need to do DMA, touch the + * LED or attempt any writes. A real floppy doesn't attempt + * to write to readonly media either. */ + fdctrl_stop_transfer(fdctrl, + FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW, + 0x00); + goto transfer_error; + } + DMA_read_memory (nchan, fdctrl->fifo + rel_pos, fdctrl->data_pos, len); if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), @@ -1289,7 +1362,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) ks = fdctrl->fifo[8]; FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect)); + fd_sector_calc(kh, kt, ks, cur_drv->last_sect, + NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ @@ -1343,7 +1417,7 @@ static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction) { fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0; fdctrl->fifo[0] = fdctrl->lock << 4; - fdctrl_set_fifo(fdctrl, 1, fdctrl->lock); + fdctrl_set_fifo(fdctrl, 1, 0); } static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction) @@ -1375,7 +1449,7 @@ static void fdctrl_handle_version(FDCtrl *fdctrl, int direction) { /* Controller's version */ fdctrl->fifo[0] = fdctrl->version; - fdctrl_set_fifo(fdctrl, 1, 1); + fdctrl_set_fifo(fdctrl, 1, 0); } static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction) @@ -1434,14 +1508,13 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) fdctrl->fifo[12] = fdctrl->pwrd; fdctrl->fifo[13] = 0; fdctrl->fifo[14] = 0; - fdctrl_set_fifo(fdctrl, 15, 1); + fdctrl_set_fifo(fdctrl, 15, 0); } static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) { FDrive *cur_drv = get_cur_drv(fdctrl); - /* XXX: should set main status register to busy */ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; qemu_mod_timer(fdctrl->result_timer, qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50)); @@ -1545,13 +1618,16 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction) SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); fdctrl_reset_fifo(fdctrl); + /* The seek command just sends step pulses to the drive and doesn't care if + * there is a medium inserted of if it's banging the head against the drive. + */ if (fdctrl->fifo[2] > cur_drv->max_track) { - fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK); + cur_drv->track = cur_drv->max_track; } else { cur_drv->track = fdctrl->fifo[2]; - /* Raise Interrupt */ - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); } + /* Raise Interrupt */ + fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); } static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction) @@ -1576,7 +1652,7 @@ static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction) { fdctrl->pwrd = fdctrl->fifo[1]; fdctrl->fifo[0] = fdctrl->fifo[1]; - fdctrl_set_fifo(fdctrl, 1, 1); + fdctrl_set_fifo(fdctrl, 1, 0); } static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) @@ -1595,7 +1671,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct fdctrl->fifo[0] = fdctrl->fifo[1]; fdctrl->fifo[2] = 0; fdctrl->fifo[3] = 0; - fdctrl_set_fifo(fdctrl, 4, 1); + fdctrl_set_fifo(fdctrl, 4, 0); } else { fdctrl_reset_fifo(fdctrl); } @@ -1603,7 +1679,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct /* ERROR */ fdctrl->fifo[0] = 0x80 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); - fdctrl_set_fifo(fdctrl, 1, 1); + fdctrl_set_fifo(fdctrl, 1, 0); } } @@ -1729,6 +1805,7 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) pos = command_to_handler[value & 0xff]; FLOPPY_DPRINTF("%s command\n", handlers[pos].name); fdctrl->data_len = handlers[pos].parameters + 1; + fdctrl->msr |= FD_MSR_CMDBUSY; } FLOPPY_DPRINTF("%s: %02x\n", __func__, value); @@ -1760,7 +1837,15 @@ static void fdctrl_result_timer(void *opaque) if (cur_drv->last_sect != 0) { cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; } - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + /* READ_ID can't automatically succeed! */ + if (fdctrl->check_media_rate && + (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { + FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n", + fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + } else { + fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + } } static void fdctrl_change_cb(void *opaque, bool load) @@ -1782,6 +1867,7 @@ static int fdctrl_connect_drives(FDCtrl *fdctrl) for (i = 0; i < MAX_FD; i++) { drive = &fdctrl->drives[i]; + drive->fdctrl = fdctrl; if (drive->bs) { if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) { @@ -1964,6 +2050,8 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs), DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1), DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1), + DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, + 0, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/grlib.h b/hw/grlib.h index fdf4b1190a..e1c41378d4 100644 --- a/hw/grlib.h +++ b/hw/grlib.h @@ -42,7 +42,7 @@ void grlib_irqmp_ack(DeviceState *dev, int intno); static inline DeviceState *grlib_irqmp_create(target_phys_addr_t base, - CPUState *env, + CPUSPARCState *env, qemu_irq **cpu_irqs, uint32_t nr_irqs, set_pil_in_fn set_pil_in) diff --git a/hw/highbank.c b/hw/highbank.c index 489c00e5b9..906eed5a47 100644 --- a/hw/highbank.c +++ b/hw/highbank.c @@ -37,12 +37,12 @@ /* Board init. */ static void highbank_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUARMState *env = opaque; env->cp15.c15_config_base_address = GIC_BASE_ADDR; } -static void hb_write_secondary(CPUState *env, const struct arm_boot_info *info) +static void hb_write_secondary(CPUARMState *env, const struct arm_boot_info *info) { int n; uint32_t smpboot[] = { @@ -66,7 +66,7 @@ static void hb_write_secondary(CPUState *env, const struct arm_boot_info *info) rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR); } -static void hb_reset_secondary(CPUState *env, const struct arm_boot_info *info) +static void hb_reset_secondary(CPUARMState *env, const struct arm_boot_info *info) { switch (info->nb_cpus) { case 4: @@ -196,7 +196,7 @@ static void highbank_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env = NULL; + CPUARMState *env = NULL; DeviceState *dev; SysBusDevice *busdev; qemu_irq *irqp; diff --git a/hw/i8254.c b/hw/i8254.c index f30396af88..77bd5e8222 100644 --- a/hw/i8254.c +++ b/hw/i8254.c @@ -26,6 +26,7 @@ #include "isa.h" #include "qemu-timer.h" #include "i8254.h" +#include "i8254_internal.h" //#define DEBUG_PIT @@ -34,34 +35,6 @@ #define RW_STATE_WORD0 3 #define RW_STATE_WORD1 4 -typedef struct PITChannelState { - int count; /* can be 65536 */ - uint16_t latched_count; - uint8_t count_latched; - uint8_t status_latched; - uint8_t status; - uint8_t read_state; - uint8_t write_state; - uint8_t write_latch; - uint8_t rw_mode; - uint8_t mode; - uint8_t bcd; /* not supported */ - uint8_t gate; /* timer start */ - int64_t count_load_time; - /* irq handling */ - int64_t next_transition_time; - QEMUTimer *irq_timer; - qemu_irq irq; - uint32_t irq_disabled; -} PITChannelState; - -typedef struct PITState { - ISADevice dev; - MemoryRegion ioports; - uint32_t iobase; - PITChannelState channels[3]; -} PITState; - static void pit_irq_timer_update(PITChannelState *s, int64_t current_time); static int pit_get_count(PITChannelState *s) @@ -89,99 +62,11 @@ static int pit_get_count(PITChannelState *s) return counter; } -/* get pit output bit */ -static int pit_get_out(PITChannelState *s, int64_t current_time) -{ - uint64_t d; - int out; - - d = muldiv64(current_time - s->count_load_time, PIT_FREQ, - get_ticks_per_sec()); - switch(s->mode) { - default: - case 0: - out = (d >= s->count); - break; - case 1: - out = (d < s->count); - break; - case 2: - if ((d % s->count) == 0 && d != 0) - out = 1; - else - out = 0; - break; - case 3: - out = (d % s->count) < ((s->count + 1) >> 1); - break; - case 4: - case 5: - out = (d == s->count); - break; - } - return out; -} - -/* return -1 if no transition will occur. */ -static int64_t pit_get_next_transition_time(PITChannelState *s, - int64_t current_time) -{ - uint64_t d, next_time, base; - int period2; - - d = muldiv64(current_time - s->count_load_time, PIT_FREQ, - get_ticks_per_sec()); - switch(s->mode) { - default: - case 0: - case 1: - if (d < s->count) - next_time = s->count; - else - return -1; - break; - case 2: - base = (d / s->count) * s->count; - if ((d - base) == 0 && d != 0) - next_time = base + s->count; - else - next_time = base + s->count + 1; - break; - case 3: - base = (d / s->count) * s->count; - period2 = ((s->count + 1) >> 1); - if ((d - base) < period2) - next_time = base + period2; - else - next_time = base + s->count; - break; - case 4: - case 5: - if (d < s->count) - next_time = s->count; - else if (d == s->count) - next_time = s->count + 1; - else - return -1; - break; - } - /* convert to timer units */ - next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(), - PIT_FREQ); - /* fix potential rounding problems */ - /* XXX: better solution: use a clock at PIT_FREQ Hz */ - if (next_time <= current_time) - next_time = current_time + 1; - return next_time; -} - /* val must be 0 or 1 */ -void pit_set_gate(ISADevice *dev, int channel, int val) +static void pit_set_channel_gate(PITCommonState *s, PITChannelState *sc, + int val) { - PITState *pit = DO_UPCAST(PITState, dev, dev); - PITChannelState *s = &pit->channels[channel]; - - switch(s->mode) { + switch (sc->mode) { default: case 0: case 4: @@ -189,34 +74,23 @@ void pit_set_gate(ISADevice *dev, int channel, int val) break; case 1: case 5: - if (s->gate < val) { + if (sc->gate < val) { /* restart counting on rising edge */ - s->count_load_time = qemu_get_clock_ns(vm_clock); - pit_irq_timer_update(s, s->count_load_time); + sc->count_load_time = qemu_get_clock_ns(vm_clock); + pit_irq_timer_update(sc, sc->count_load_time); } break; case 2: case 3: - if (s->gate < val) { + if (sc->gate < val) { /* restart counting on rising edge */ - s->count_load_time = qemu_get_clock_ns(vm_clock); - pit_irq_timer_update(s, s->count_load_time); + sc->count_load_time = qemu_get_clock_ns(vm_clock); + pit_irq_timer_update(sc, sc->count_load_time); } /* XXX: disable/enable counting */ break; } - s->gate = val; -} - -void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info) -{ - PITState *pit = DO_UPCAST(PITState, dev, dev); - PITChannelState *s = &pit->channels[channel]; - - info->gate = s->gate; - info->mode = s->mode; - info->initial_count = s->count; - info->out = pit_get_out(s, qemu_get_clock_ns(vm_clock)); + sc->gate = val; } static inline void pit_load_count(PITChannelState *s, int val) @@ -239,7 +113,7 @@ static void pit_latch_count(PITChannelState *s) static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) { - PITState *pit = opaque; + PITCommonState *pit = opaque; int channel, access; PITChannelState *s; @@ -306,7 +180,7 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) static uint32_t pit_ioport_read(void *opaque, uint32_t addr) { - PITState *pit = opaque; + PITCommonState *pit = opaque; int ret, count; PITChannelState *s; @@ -387,94 +261,16 @@ static void pit_irq_timer(void *opaque) pit_irq_timer_update(s, s->next_transition_time); } -static const VMStateDescription vmstate_pit_channel = { - .name = "pit channel", - .version_id = 2, - .minimum_version_id = 2, - .minimum_version_id_old = 2, - .fields = (VMStateField []) { - VMSTATE_INT32(count, PITChannelState), - VMSTATE_UINT16(latched_count, PITChannelState), - VMSTATE_UINT8(count_latched, PITChannelState), - VMSTATE_UINT8(status_latched, PITChannelState), - VMSTATE_UINT8(status, PITChannelState), - VMSTATE_UINT8(read_state, PITChannelState), - VMSTATE_UINT8(write_state, PITChannelState), - VMSTATE_UINT8(write_latch, PITChannelState), - VMSTATE_UINT8(rw_mode, PITChannelState), - VMSTATE_UINT8(mode, PITChannelState), - VMSTATE_UINT8(bcd, PITChannelState), - VMSTATE_UINT8(gate, PITChannelState), - VMSTATE_INT64(count_load_time, PITChannelState), - VMSTATE_INT64(next_transition_time, PITChannelState), - VMSTATE_END_OF_LIST() - } -}; - -static int pit_load_old(QEMUFile *f, void *opaque, int version_id) +static void pit_reset(DeviceState *dev) { - PITState *pit = opaque; + PITCommonState *pit = DO_UPCAST(PITCommonState, dev.qdev, dev); PITChannelState *s; - int i; - - if (version_id != 1) - return -EINVAL; - - for(i = 0; i < 3; i++) { - s = &pit->channels[i]; - s->count=qemu_get_be32(f); - qemu_get_be16s(f, &s->latched_count); - qemu_get_8s(f, &s->count_latched); - qemu_get_8s(f, &s->status_latched); - qemu_get_8s(f, &s->status); - qemu_get_8s(f, &s->read_state); - qemu_get_8s(f, &s->write_state); - qemu_get_8s(f, &s->write_latch); - qemu_get_8s(f, &s->rw_mode); - qemu_get_8s(f, &s->mode); - qemu_get_8s(f, &s->bcd); - qemu_get_8s(f, &s->gate); - s->count_load_time=qemu_get_be64(f); - s->irq_disabled = 0; - if (s->irq_timer) { - s->next_transition_time=qemu_get_be64(f); - qemu_get_timer(f, s->irq_timer); - } - } - return 0; -} -static const VMStateDescription vmstate_pit = { - .name = "i8254", - .version_id = 3, - .minimum_version_id = 2, - .minimum_version_id_old = 1, - .load_state_old = pit_load_old, - .fields = (VMStateField []) { - VMSTATE_UINT32_V(channels[0].irq_disabled, PITState, 3), - VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState), - VMSTATE_TIMER(channels[0].irq_timer, PITState), - VMSTATE_END_OF_LIST() - } -}; + pit_reset_common(pit); -static void pit_reset(DeviceState *dev) -{ - PITState *pit = container_of(dev, PITState, dev.qdev); - PITChannelState *s; - int i; - - for(i = 0;i < 3; i++) { - s = &pit->channels[i]; - s->mode = 3; - s->gate = (i != 2); - s->count_load_time = qemu_get_clock_ns(vm_clock); - s->count = 0x10000; - if (i == 0 && !s->irq_disabled) { - s->next_transition_time = - pit_get_next_transition_time(s, s->count_load_time); - qemu_mod_timer(s->irq_timer, s->next_transition_time); - } + s = &pit->channels[0]; + if (!s->irq_disabled) { + qemu_mod_timer(s->irq_timer, s->next_transition_time); } } @@ -482,7 +278,7 @@ static void pit_reset(DeviceState *dev) * reenable it when legacy mode is left again. */ static void pit_irq_control(void *opaque, int n, int enable) { - PITState *pit = opaque; + PITCommonState *pit = opaque; PITChannelState *s = &pit->channels[0]; if (enable) { @@ -504,46 +300,55 @@ static const MemoryRegionOps pit_ioport_ops = { .old_portio = pit_portio }; -static int pit_initfn(ISADevice *dev) +static void pit_post_load(PITCommonState *s) +{ + PITChannelState *sc = &s->channels[0]; + + if (sc->next_transition_time != -1) { + qemu_mod_timer(sc->irq_timer, sc->next_transition_time); + } else { + qemu_del_timer(sc->irq_timer); + } +} + +static int pit_initfn(PITCommonState *pit) { - PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s; s = &pit->channels[0]; /* the timer 0 is connected to an IRQ */ s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s); - qdev_init_gpio_out(&dev->qdev, &s->irq, 1); + qdev_init_gpio_out(&pit->dev.qdev, &s->irq, 1); memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4); - isa_register_ioport(dev, &pit->ioports, pit->iobase); - qdev_init_gpio_in(&dev->qdev, pit_irq_control, 1); - - qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2); + qdev_init_gpio_in(&pit->dev.qdev, pit_irq_control, 1); return 0; } static Property pit_properties[] = { - DEFINE_PROP_HEX32("iobase", PITState, iobase, -1), + DEFINE_PROP_HEX32("iobase", PITCommonState, iobase, -1), DEFINE_PROP_END_OF_LIST(), }; static void pit_class_initfn(ObjectClass *klass, void *data) { + PITCommonClass *k = PIT_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); - ic->init = pit_initfn; - dc->no_user = 1; + + k->init = pit_initfn; + k->set_channel_gate = pit_set_channel_gate; + k->get_channel_info = pit_get_channel_info_common; + k->post_load = pit_post_load; dc->reset = pit_reset; - dc->vmsd = &vmstate_pit; dc->props = pit_properties; } static TypeInfo pit_info = { .name = "isa-pit", - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(PITState), + .parent = TYPE_PIT_COMMON, + .instance_size = sizeof(PITCommonState), .class_init = pit_class_initfn, }; diff --git a/hw/i8254.h b/hw/i8254.h index a1d2e9835b..ba6b598a99 100644 --- a/hw/i8254.h +++ b/hw/i8254.h @@ -51,6 +51,17 @@ static inline ISADevice *pit_init(ISABus *bus, int base, int isa_irq, return dev; } +static inline ISADevice *kvm_pit_init(ISABus *bus, int base) +{ + ISADevice *dev; + + dev = isa_create(bus, "kvm-pit"); + qdev_prop_set_uint32(&dev->qdev, "iobase", base); + qdev_init_nofail(&dev->qdev); + + return dev; +} + void pit_set_gate(ISADevice *dev, int channel, int val); void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info); diff --git a/hw/i8254_common.c b/hw/i8254_common.c new file mode 100644 index 0000000000..a03d7cd458 --- /dev/null +++ b/hw/i8254_common.c @@ -0,0 +1,311 @@ +/* + * QEMU 8253/8254 - common bits of emulated and KVM kernel model + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2012 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "pc.h" +#include "isa.h" +#include "qemu-timer.h" +#include "i8254.h" +#include "i8254_internal.h" + +/* val must be 0 or 1 */ +void pit_set_gate(ISADevice *dev, int channel, int val) +{ + PITCommonState *pit = PIT_COMMON(dev); + PITChannelState *s = &pit->channels[channel]; + PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); + + c->set_channel_gate(pit, s, val); +} + +/* get pit output bit */ +int pit_get_out(PITChannelState *s, int64_t current_time) +{ + uint64_t d; + int out; + + d = muldiv64(current_time - s->count_load_time, PIT_FREQ, + get_ticks_per_sec()); + switch (s->mode) { + default: + case 0: + out = (d >= s->count); + break; + case 1: + out = (d < s->count); + break; + case 2: + if ((d % s->count) == 0 && d != 0) { + out = 1; + } else { + out = 0; + } + break; + case 3: + out = (d % s->count) < ((s->count + 1) >> 1); + break; + case 4: + case 5: + out = (d == s->count); + break; + } + return out; +} + +/* return -1 if no transition will occur. */ +int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time) +{ + uint64_t d, next_time, base; + int period2; + + d = muldiv64(current_time - s->count_load_time, PIT_FREQ, + get_ticks_per_sec()); + switch (s->mode) { + default: + case 0: + case 1: + if (d < s->count) { + next_time = s->count; + } else { + return -1; + } + break; + case 2: + base = (d / s->count) * s->count; + if ((d - base) == 0 && d != 0) { + next_time = base + s->count; + } else { + next_time = base + s->count + 1; + } + break; + case 3: + base = (d / s->count) * s->count; + period2 = ((s->count + 1) >> 1); + if ((d - base) < period2) { + next_time = base + period2; + } else { + next_time = base + s->count; + } + break; + case 4: + case 5: + if (d < s->count) { + next_time = s->count; + } else if (d == s->count) { + next_time = s->count + 1; + } else { + return -1; + } + break; + } + /* convert to timer units */ + next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(), + PIT_FREQ); + /* fix potential rounding problems */ + /* XXX: better solution: use a clock at PIT_FREQ Hz */ + if (next_time <= current_time) { + next_time = current_time + 1; + } + return next_time; +} + +void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc, + PITChannelInfo *info) +{ + info->gate = sc->gate; + info->mode = sc->mode; + info->initial_count = sc->count; + info->out = pit_get_out(sc, qemu_get_clock_ns(vm_clock)); +} + +void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info) +{ + PITCommonState *pit = PIT_COMMON(dev); + PITChannelState *s = &pit->channels[channel]; + PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); + + c->get_channel_info(pit, s, info); +} + +void pit_reset_common(PITCommonState *pit) +{ + PITChannelState *s; + int i; + + for (i = 0; i < 3; i++) { + s = &pit->channels[i]; + s->mode = 3; + s->gate = (i != 2); + s->count_load_time = qemu_get_clock_ns(vm_clock); + s->count = 0x10000; + if (i == 0 && !s->irq_disabled) { + s->next_transition_time = + pit_get_next_transition_time(s, s->count_load_time); + } + } +} + +static int pit_init_common(ISADevice *dev) +{ + PITCommonState *pit = PIT_COMMON(dev); + PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); + int ret; + + ret = c->init(pit); + if (ret < 0) { + return ret; + } + + isa_register_ioport(dev, &pit->ioports, pit->iobase); + + qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2); + + return 0; +} + +static const VMStateDescription vmstate_pit_channel = { + .name = "pit channel", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField[]) { + VMSTATE_INT32(count, PITChannelState), + VMSTATE_UINT16(latched_count, PITChannelState), + VMSTATE_UINT8(count_latched, PITChannelState), + VMSTATE_UINT8(status_latched, PITChannelState), + VMSTATE_UINT8(status, PITChannelState), + VMSTATE_UINT8(read_state, PITChannelState), + VMSTATE_UINT8(write_state, PITChannelState), + VMSTATE_UINT8(write_latch, PITChannelState), + VMSTATE_UINT8(rw_mode, PITChannelState), + VMSTATE_UINT8(mode, PITChannelState), + VMSTATE_UINT8(bcd, PITChannelState), + VMSTATE_UINT8(gate, PITChannelState), + VMSTATE_INT64(count_load_time, PITChannelState), + VMSTATE_INT64(next_transition_time, PITChannelState), + VMSTATE_END_OF_LIST() + } +}; + +static int pit_load_old(QEMUFile *f, void *opaque, int version_id) +{ + PITCommonState *pit = opaque; + PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); + PITChannelState *s; + int i; + + if (version_id != 1) { + return -EINVAL; + } + + for (i = 0; i < 3; i++) { + s = &pit->channels[i]; + s->count = qemu_get_be32(f); + qemu_get_be16s(f, &s->latched_count); + qemu_get_8s(f, &s->count_latched); + qemu_get_8s(f, &s->status_latched); + qemu_get_8s(f, &s->status); + qemu_get_8s(f, &s->read_state); + qemu_get_8s(f, &s->write_state); + qemu_get_8s(f, &s->write_latch); + qemu_get_8s(f, &s->rw_mode); + qemu_get_8s(f, &s->mode); + qemu_get_8s(f, &s->bcd); + qemu_get_8s(f, &s->gate); + s->count_load_time = qemu_get_be64(f); + s->irq_disabled = 0; + if (i == 0) { + s->next_transition_time = qemu_get_be64(f); + } + } + if (c->post_load) { + c->post_load(pit); + } + return 0; +} + +static void pit_dispatch_pre_save(void *opaque) +{ + PITCommonState *s = opaque; + PITCommonClass *c = PIT_COMMON_GET_CLASS(s); + + if (c->pre_save) { + c->pre_save(s); + } +} + +static int pit_dispatch_post_load(void *opaque, int version_id) +{ + PITCommonState *s = opaque; + PITCommonClass *c = PIT_COMMON_GET_CLASS(s); + + if (c->post_load) { + c->post_load(s); + } + return 0; +} + +static const VMStateDescription vmstate_pit_common = { + .name = "i8254", + .version_id = 3, + .minimum_version_id = 2, + .minimum_version_id_old = 1, + .load_state_old = pit_load_old, + .pre_save = pit_dispatch_pre_save, + .post_load = pit_dispatch_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3), + VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2, + vmstate_pit_channel, PITChannelState), + VMSTATE_INT64(channels[0].next_transition_time, + PITCommonState), /* formerly irq_timer */ + VMSTATE_END_OF_LIST() + } +}; + +static void pit_common_class_init(ObjectClass *klass, void *data) +{ + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + ic->init = pit_init_common; + dc->vmsd = &vmstate_pit_common; + dc->no_user = 1; +} + +static TypeInfo pit_common_type = { + .name = TYPE_PIT_COMMON, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(PITCommonState), + .class_size = sizeof(PITCommonClass), + .class_init = pit_common_class_init, + .abstract = true, +}; + +static void register_devices(void) +{ + type_register_static(&pit_common_type); +} + +type_init(register_devices); diff --git a/hw/i8254_internal.h b/hw/i8254_internal.h new file mode 100644 index 0000000000..686f0c2ba9 --- /dev/null +++ b/hw/i8254_internal.h @@ -0,0 +1,85 @@ +/* + * QEMU 8253/8254 - internal interfaces + * + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_I8254_INTERNAL_H +#define QEMU_I8254_INTERNAL_H + +#include "hw.h" +#include "pc.h" +#include "isa.h" + +typedef struct PITChannelState { + int count; /* can be 65536 */ + uint16_t latched_count; + uint8_t count_latched; + uint8_t status_latched; + uint8_t status; + uint8_t read_state; + uint8_t write_state; + uint8_t write_latch; + uint8_t rw_mode; + uint8_t mode; + uint8_t bcd; /* not supported */ + uint8_t gate; /* timer start */ + int64_t count_load_time; + /* irq handling */ + int64_t next_transition_time; + QEMUTimer *irq_timer; + qemu_irq irq; + uint32_t irq_disabled; +} PITChannelState; + +typedef struct PITCommonState { + ISADevice dev; + MemoryRegion ioports; + uint32_t iobase; + PITChannelState channels[3]; +} PITCommonState; + +#define TYPE_PIT_COMMON "pit-common" +#define PIT_COMMON(obj) \ + OBJECT_CHECK(PITCommonState, (obj), TYPE_PIT_COMMON) +#define PIT_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(PITCommonClass, (klass), TYPE_PIT_COMMON) +#define PIT_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PITCommonClass, (obj), TYPE_PIT_COMMON) + +typedef struct PITCommonClass { + ISADeviceClass parent_class; + + int (*init)(PITCommonState *s); + void (*set_channel_gate)(PITCommonState *s, PITChannelState *sc, int val); + void (*get_channel_info)(PITCommonState *s, PITChannelState *sc, + PITChannelInfo *info); + void (*pre_save)(PITCommonState *s); + void (*post_load)(PITCommonState *s); +} PITCommonClass; + +int pit_get_out(PITChannelState *s, int64_t current_time); +int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time); +void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc, + PITChannelInfo *info); +void pit_reset_common(PITCommonState *s); + +#endif /* !QEMU_I8254_INTERNAL_H */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 041ce1e89f..a883a920be 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -367,7 +367,7 @@ static void ahci_mem_write(void *opaque, target_phys_addr_t addr, } -static MemoryRegionOps ahci_mem_ops = { +static const MemoryRegionOps ahci_mem_ops = { .read = ahci_mem_read, .write = ahci_mem_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -403,7 +403,7 @@ static void ahci_idp_write(void *opaque, target_phys_addr_t addr, } } -static MemoryRegionOps ahci_idp_ops = { +static const MemoryRegionOps ahci_idp_ops = { .read = ahci_idp_read, .write = ahci_idp_write, .endianness = DEVICE_LITTLE_ENDIAN, diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 743ec02406..bf8ece4708 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -65,7 +65,7 @@ static void cmd646_cmd_write(void *opaque, target_phys_addr_t addr, ide_cmd_write(cmd646bar->bus, addr + 2, data); } -static MemoryRegionOps cmd646_cmd_ops = { +static const MemoryRegionOps cmd646_cmd_ops = { .read = cmd646_cmd_read, .write = cmd646_cmd_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -104,7 +104,7 @@ static void cmd646_data_write(void *opaque, target_phys_addr_t addr, } } -static MemoryRegionOps cmd646_data_ops = { +static const MemoryRegionOps cmd646_data_ops = { .read = cmd646_data_read, .write = cmd646_data_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -193,7 +193,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr, } } -static MemoryRegionOps cmd646_bmdma_ops = { +static const MemoryRegionOps cmd646_bmdma_ops = { .read = bmdma_read, .write = bmdma_write, }; diff --git a/hw/ide/core.c b/hw/ide/core.c index ce570a7ce5..4d568acc9c 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1068,6 +1068,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */ goto abort_cmd; } + if (!s->bs) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = 1; ide_sector_read(s); @@ -1078,6 +1081,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) case WIN_WRITE_ONCE: case CFA_WRITE_SECT_WO_ERASE: case WIN_WRITE_VERIFY: + if (!s->bs) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->error = 0; s->status = SEEK_STAT | READY_STAT; @@ -1088,8 +1094,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) case WIN_MULTREAD_EXT: lba48 = 1; case WIN_MULTREAD: - if (!s->mult_sectors) + if (!s->bs) { goto abort_cmd; + } + if (!s->mult_sectors) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = s->mult_sectors; ide_sector_read(s); @@ -1098,8 +1108,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) lba48 = 1; case WIN_MULTWRITE: case CFA_WRITE_MULTI_WO_ERASE: - if (!s->mult_sectors) + if (!s->bs) { goto abort_cmd; + } + if (!s->mult_sectors) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->error = 0; s->status = SEEK_STAT | READY_STAT; @@ -1114,8 +1128,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) lba48 = 1; case WIN_READDMA: case WIN_READDMA_ONCE: - if (!s->bs) + if (!s->bs) { goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); ide_sector_start_dma(s, IDE_DMA_READ); break; @@ -1123,8 +1138,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) lba48 = 1; case WIN_WRITEDMA: case WIN_WRITEDMA_ONCE: - if (!s->bs) + if (!s->bs) { goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); ide_sector_start_dma(s, IDE_DMA_WRITE); s->media_changed = 1; diff --git a/hw/ide/macio.c b/hw/ide/macio.c index abbc41b59e..a4df24406a 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -291,7 +291,7 @@ static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr) return retval; } -static MemoryRegionOps pmac_ide_ops = { +static const MemoryRegionOps pmac_ide_ops = { .old_mmio = { .write = { pmac_ide_writeb, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 1030fcc31c..bcaa400e2d 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -79,7 +79,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr, } } -static MemoryRegionOps piix_bmdma_ops = { +static const MemoryRegionOps piix_bmdma_ops = { .read = bmdma_read, .write = bmdma_write, }; diff --git a/hw/ide/via.c b/hw/ide/via.c index 2886bc6dfb..eec5136019 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -82,7 +82,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr, } } -static MemoryRegionOps via_bmdma_ops = { +static const MemoryRegionOps via_bmdma_ops = { .read = bmdma_read, .write = bmdma_write, }; diff --git a/hw/integratorcp.c b/hw/integratorcp.c index 5b06c81c9b..9bdb9e62d6 100644 --- a/hw/integratorcp.c +++ b/hw/integratorcp.c @@ -443,7 +443,7 @@ static void integratorcp_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUARMState *env; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1); diff --git a/hw/ioapic.c b/hw/ioapic.c index 3fee0114d9..e2e4796bb5 100644 --- a/hw/ioapic.c +++ b/hw/ioapic.c @@ -195,7 +195,7 @@ ioapic_mem_write(void *opaque, target_phys_addr_t addr, uint64_t val, if (size != 4) { break; } - DPRINTF("write: %08x = %08x\n", s->ioregsel, val); + DPRINTF("write: %08x = %08" PRIx64 "\n", s->ioregsel, val); switch (s->ioregsel) { case IOAPIC_REG_ID: s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK; diff --git a/hw/kvm/apic.c b/hw/kvm/apic.c index 5bb0a4b9fd..ffe7a521b7 100644 --- a/hw/kvm/apic.c +++ b/hw/kvm/apic.c @@ -92,10 +92,39 @@ static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val) s->tpr = (val & 0x0f) << 4; } +static uint8_t kvm_apic_get_tpr(APICCommonState *s) +{ + return s->tpr >> 4; +} + +static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable) +{ + struct kvm_tpr_access_ctl ctl = { + .enabled = enable + }; + + kvm_vcpu_ioctl(s->cpu_env, KVM_TPR_ACCESS_REPORTING, &ctl); +} + +static void kvm_apic_vapic_base_update(APICCommonState *s) +{ + struct kvm_vapic_addr vapid_addr = { + .vapic_addr = s->vapic_paddr, + }; + int ret; + + ret = kvm_vcpu_ioctl(s->cpu_env, KVM_SET_VAPIC_ADDR, &vapid_addr); + if (ret < 0) { + fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n", + strerror(-ret)); + abort(); + } +} + static void do_inject_external_nmi(void *data) { APICCommonState *s = data; - CPUState *env = s->cpu_env; + CPUX86State *env = s->cpu_env; uint32_t lvt; int ret; @@ -129,6 +158,9 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data) k->init = kvm_apic_init; k->set_base = kvm_apic_set_base; k->set_tpr = kvm_apic_set_tpr; + k->get_tpr = kvm_apic_get_tpr; + k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting; + k->vapic_base_update = kvm_apic_vapic_base_update; k->external_nmi = kvm_apic_external_nmi; } diff --git a/hw/kvm/clock.c b/hw/kvm/clock.c index 2157340326..446bd62176 100644 --- a/hw/kvm/clock.c +++ b/hw/kvm/clock.c @@ -121,9 +121,7 @@ void kvmclock_create(void) static void kvmclock_register_types(void) { - if (kvm_enabled()) { type_register_static(&kvmclock_info); - } } type_init(kvmclock_register_types) diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c new file mode 100644 index 0000000000..bb5fe07d1e --- /dev/null +++ b/hw/kvm/i8254.c @@ -0,0 +1,254 @@ +/* + * KVM in-kernel PIT (i8254) support + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2012 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-timer.h" +#include "hw/i8254.h" +#include "hw/i8254_internal.h" +#include "kvm.h" + +#define KVM_PIT_REINJECT_BIT 0 + +typedef struct KVMPITState { + PITCommonState pit; + LostTickPolicy lost_tick_policy; +} KVMPITState; + +static void kvm_pit_get(PITCommonState *s) +{ + struct kvm_pit_state2 kpit; + struct kvm_pit_channel_state *kchan; + struct PITChannelState *sc; + int i, ret; + + if (kvm_has_pit_state2()) { + ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); + if (ret < 0) { + fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret)); + abort(); + } + s->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; + } else { + /* + * kvm_pit_state2 is superset of kvm_pit_state struct, + * so we can use it for KVM_GET_PIT as well. + */ + ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); + if (ret < 0) { + fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(ret)); + abort(); + } + } + for (i = 0; i < 3; i++) { + kchan = &kpit.channels[i]; + sc = &s->channels[i]; + sc->count = kchan->count; + sc->latched_count = kchan->latched_count; + sc->count_latched = kchan->count_latched; + sc->status_latched = kchan->status_latched; + sc->status = kchan->status; + sc->read_state = kchan->read_state; + sc->write_state = kchan->write_state; + sc->write_latch = kchan->write_latch; + sc->rw_mode = kchan->rw_mode; + sc->mode = kchan->mode; + sc->bcd = kchan->bcd; + sc->gate = kchan->gate; + sc->count_load_time = kchan->count_load_time; + } + + sc = &s->channels[0]; + sc->next_transition_time = + pit_get_next_transition_time(sc, sc->count_load_time); +} + +static void kvm_pit_put(PITCommonState *s) +{ + 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; + for (i = 0; i < 3; i++) { + kchan = &kpit.channels[i]; + sc = &s->channels[i]; + kchan->count = sc->count; + kchan->latched_count = sc->latched_count; + kchan->count_latched = sc->count_latched; + kchan->status_latched = sc->status_latched; + kchan->status = sc->status; + kchan->read_state = sc->read_state; + kchan->write_state = sc->write_state; + kchan->write_latch = sc->write_latch; + kchan->rw_mode = sc->rw_mode; + kchan->mode = sc->mode; + kchan->bcd = sc->bcd; + kchan->gate = sc->gate; + kchan->count_load_time = sc->count_load_time; + } + + ret = kvm_vm_ioctl(kvm_state, + kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, + &kpit); + if (ret < 0) { + fprintf(stderr, "%s failed: %s\n", + kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", + strerror(ret)); + abort(); + } +} + +static void kvm_pit_set_gate(PITCommonState *s, PITChannelState *sc, int val) +{ + kvm_pit_get(s); + + switch (sc->mode) { + default: + case 0: + case 4: + /* XXX: just disable/enable counting */ + break; + case 1: + case 2: + case 3: + case 5: + if (sc->gate < val) { + /* restart counting on rising edge */ + sc->count_load_time = qemu_get_clock_ns(vm_clock); + } + break; + } + sc->gate = val; + + kvm_pit_put(s); +} + +static void kvm_pit_get_channel_info(PITCommonState *s, PITChannelState *sc, + PITChannelInfo *info) +{ + kvm_pit_get(s); + + pit_get_channel_info_common(s, sc, info); +} + +static void kvm_pit_reset(DeviceState *dev) +{ + PITCommonState *s = DO_UPCAST(PITCommonState, dev.qdev, dev); + + pit_reset_common(s); + + kvm_pit_put(s); +} + +static void kvm_pit_irq_control(void *opaque, int n, int enable) +{ + PITCommonState *pit = opaque; + PITChannelState *s = &pit->channels[0]; + + kvm_pit_get(pit); + + s->irq_disabled = !enable; + + kvm_pit_put(pit); +} + +static int kvm_pit_initfn(PITCommonState *pit) +{ + KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); + struct kvm_pit_config config = { + .flags = 0, + }; + int ret; + + if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { + ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); + } else { + ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); + } + if (ret < 0) { + fprintf(stderr, "Create kernel PIC irqchip failed: %s\n", + strerror(ret)); + return ret; + } + switch (s->lost_tick_policy) { + case LOST_TICK_DELAY: + break; /* enabled by default */ + case LOST_TICK_DISCARD: + if (kvm_check_extension(kvm_state, KVM_CAP_REINJECT_CONTROL)) { + struct kvm_reinject_control control = { .pit_reinject = 0 }; + + ret = kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); + if (ret < 0) { + fprintf(stderr, + "Can't disable in-kernel PIT reinjection: %s\n", + strerror(ret)); + return ret; + } + } + break; + default: + return -EINVAL; + } + + memory_region_init_reservation(&pit->ioports, "kvm-pit", 4); + + qdev_init_gpio_in(&pit->dev.qdev, kvm_pit_irq_control, 1); + + return 0; +} + +static Property kvm_pit_properties[] = { + DEFINE_PROP_HEX32("iobase", KVMPITState, pit.iobase, -1), + DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, + lost_tick_policy, LOST_TICK_DELAY), + DEFINE_PROP_END_OF_LIST(), +}; + +static void kvm_pit_class_init(ObjectClass *klass, void *data) +{ + PITCommonClass *k = PIT_COMMON_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = kvm_pit_initfn; + k->set_channel_gate = kvm_pit_set_gate; + k->get_channel_info = kvm_pit_get_channel_info; + k->pre_save = kvm_pit_get; + k->post_load = kvm_pit_put; + dc->reset = kvm_pit_reset; + dc->props = kvm_pit_properties; +} + +static TypeInfo kvm_pit_info = { + .name = "kvm-pit", + .parent = TYPE_PIT_COMMON, + .instance_size = sizeof(KVMPITState), + .class_init = kvm_pit_class_init, +}; + +static void kvm_pit_register(void) +{ + type_register_static(&kvm_pit_info); +} + +type_init(kvm_pit_register) diff --git a/hw/kvmvapic.c b/hw/kvmvapic.c new file mode 100644 index 0000000000..5d83625f4a --- /dev/null +++ b/hw/kvmvapic.c @@ -0,0 +1,807 @@ +/* + * TPR optimization for 32-bit Windows guests (XP and Server 2003) + * + * Copyright (C) 2007-2008 Qumranet Technologies + * Copyright (C) 2012 Jan Kiszka, Siemens AG + * + * This work is licensed under the terms of the GNU GPL version 2, or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "sysemu.h" +#include "cpus.h" +#include "kvm.h" +#include "apic_internal.h" + +#define APIC_DEFAULT_ADDRESS 0xfee00000 + +#define VAPIC_IO_PORT 0x7e + +#define VAPIC_CPU_SHIFT 7 + +#define ROM_BLOCK_SIZE 512 +#define ROM_BLOCK_MASK (~(ROM_BLOCK_SIZE - 1)) + +typedef enum VAPICMode { + VAPIC_INACTIVE = 0, + VAPIC_ACTIVE = 1, + VAPIC_STANDBY = 2, +} VAPICMode; + +typedef struct VAPICHandlers { + uint32_t set_tpr; + uint32_t set_tpr_eax; + uint32_t get_tpr[8]; + uint32_t get_tpr_stack; +} QEMU_PACKED VAPICHandlers; + +typedef struct GuestROMState { + char signature[8]; + uint32_t vaddr; + uint32_t fixup_start; + uint32_t fixup_end; + uint32_t vapic_vaddr; + uint32_t vapic_size; + uint32_t vcpu_shift; + uint32_t real_tpr_addr; + VAPICHandlers up; + VAPICHandlers mp; +} QEMU_PACKED GuestROMState; + +typedef struct VAPICROMState { + SysBusDevice busdev; + MemoryRegion io; + MemoryRegion rom; + uint32_t state; + uint32_t rom_state_paddr; + uint32_t rom_state_vaddr; + uint32_t vapic_paddr; + uint32_t real_tpr_addr; + GuestROMState rom_state; + size_t rom_size; + bool rom_mapped_writable; +} VAPICROMState; + +#define TPR_INSTR_ABS_MODRM 0x1 +#define TPR_INSTR_MATCH_MODRM_REG 0x2 + +typedef struct TPRInstruction { + uint8_t opcode; + uint8_t modrm_reg; + unsigned int flags; + TPRAccess access; + size_t length; + off_t addr_offset; +} TPRInstruction; + +/* must be sorted by length, shortest first */ +static const TPRInstruction tpr_instr[] = { + { /* mov abs to eax */ + .opcode = 0xa1, + .access = TPR_ACCESS_READ, + .length = 5, + .addr_offset = 1, + }, + { /* mov eax to abs */ + .opcode = 0xa3, + .access = TPR_ACCESS_WRITE, + .length = 5, + .addr_offset = 1, + }, + { /* mov r32 to r/m32 */ + .opcode = 0x89, + .flags = TPR_INSTR_ABS_MODRM, + .access = TPR_ACCESS_WRITE, + .length = 6, + .addr_offset = 2, + }, + { /* mov r/m32 to r32 */ + .opcode = 0x8b, + .flags = TPR_INSTR_ABS_MODRM, + .access = TPR_ACCESS_READ, + .length = 6, + .addr_offset = 2, + }, + { /* push r/m32 */ + .opcode = 0xff, + .modrm_reg = 6, + .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, + .access = TPR_ACCESS_READ, + .length = 6, + .addr_offset = 2, + }, + { /* mov imm32, r/m32 (c7/0) */ + .opcode = 0xc7, + .modrm_reg = 0, + .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, + .access = TPR_ACCESS_WRITE, + .length = 10, + .addr_offset = 2, + }, +}; + +static void read_guest_rom_state(VAPICROMState *s) +{ + cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state, + sizeof(GuestROMState), 0); +} + +static void write_guest_rom_state(VAPICROMState *s) +{ + cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state, + sizeof(GuestROMState), 1); +} + +static void update_guest_rom_state(VAPICROMState *s) +{ + read_guest_rom_state(s); + + s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr); + s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT); + + write_guest_rom_state(s); +} + +static int find_real_tpr_addr(VAPICROMState *s, CPUX86State *env) +{ + target_phys_addr_t paddr; + target_ulong addr; + + if (s->state == VAPIC_ACTIVE) { + return 0; + } + /* + * If there is no prior TPR access instruction we could analyze (which is + * the case after resume from hibernation), we need to scan the possible + * virtual address space for the APIC mapping. + */ + for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) { + paddr = cpu_get_phys_page_debug(env, addr); + if (paddr != APIC_DEFAULT_ADDRESS) { + continue; + } + s->real_tpr_addr = addr + 0x80; + update_guest_rom_state(s); + return 0; + } + return -1; +} + +static uint8_t modrm_reg(uint8_t modrm) +{ + return (modrm >> 3) & 7; +} + +static bool is_abs_modrm(uint8_t modrm) +{ + return (modrm & 0xc7) == 0x05; +} + +static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr) +{ + return opcode[0] == instr->opcode && + (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) && + (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) || + modrm_reg(opcode[1]) == instr->modrm_reg); +} + +static int evaluate_tpr_instruction(VAPICROMState *s, CPUX86State *env, + target_ulong *pip, TPRAccess access) +{ + const TPRInstruction *instr; + target_ulong ip = *pip; + uint8_t opcode[2]; + uint32_t real_tpr_addr; + int i; + + if ((ip & 0xf0000000ULL) != 0x80000000ULL && + (ip & 0xf0000000ULL) != 0xe0000000ULL) { + return -1; + } + + /* + * Early Windows 2003 SMP initialization contains a + * + * mov imm32, r/m32 + * + * instruction that is patched by TPR optimization. The problem is that + * RSP, used by the patched instruction, is zero, so the guest gets a + * double fault and dies. + */ + if (env->regs[R_ESP] == 0) { + return -1; + } + + if (kvm_enabled() && !kvm_irqchip_in_kernel()) { + /* + * KVM without kernel-based TPR access reporting will pass an IP that + * points after the accessing instruction. So we need to look backward + * to find the reason. + */ + for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { + instr = &tpr_instr[i]; + if (instr->access != access) { + continue; + } + if (cpu_memory_rw_debug(env, ip - instr->length, opcode, + sizeof(opcode), 0) < 0) { + return -1; + } + if (opcode_matches(opcode, instr)) { + ip -= instr->length; + goto instruction_ok; + } + } + return -1; + } else { + if (cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0) < 0) { + return -1; + } + for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { + instr = &tpr_instr[i]; + if (opcode_matches(opcode, instr)) { + goto instruction_ok; + } + } + return -1; + } + +instruction_ok: + /* + * Grab the virtual TPR address from the instruction + * and update the cached values. + */ + if (cpu_memory_rw_debug(env, ip + instr->addr_offset, + (void *)&real_tpr_addr, + sizeof(real_tpr_addr), 0) < 0) { + return -1; + } + real_tpr_addr = le32_to_cpu(real_tpr_addr); + if ((real_tpr_addr & 0xfff) != 0x80) { + return -1; + } + s->real_tpr_addr = real_tpr_addr; + update_guest_rom_state(s); + + *pip = ip; + return 0; +} + +static int update_rom_mapping(VAPICROMState *s, CPUX86State *env, target_ulong ip) +{ + target_phys_addr_t paddr; + uint32_t rom_state_vaddr; + uint32_t pos, patch, offset; + + /* nothing to do if already activated */ + if (s->state == VAPIC_ACTIVE) { + return 0; + } + + /* bail out if ROM init code was not executed (missing ROM?) */ + if (s->state == VAPIC_INACTIVE) { + return -1; + } + + /* find out virtual address of the ROM */ + rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000); + paddr = cpu_get_phys_page_debug(env, rom_state_vaddr); + if (paddr == -1) { + return -1; + } + paddr += rom_state_vaddr & ~TARGET_PAGE_MASK; + if (paddr != s->rom_state_paddr) { + return -1; + } + read_guest_rom_state(s); + if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) { + return -1; + } + s->rom_state_vaddr = rom_state_vaddr; + + /* fixup addresses in ROM if needed */ + if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) { + return 0; + } + for (pos = le32_to_cpu(s->rom_state.fixup_start); + pos < le32_to_cpu(s->rom_state.fixup_end); + pos += 4) { + cpu_physical_memory_rw(paddr + pos - s->rom_state.vaddr, + (void *)&offset, sizeof(offset), 0); + offset = le32_to_cpu(offset); + cpu_physical_memory_rw(paddr + offset, (void *)&patch, + sizeof(patch), 0); + patch = le32_to_cpu(patch); + patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr); + patch = cpu_to_le32(patch); + cpu_physical_memory_rw(paddr + offset, (void *)&patch, + sizeof(patch), 1); + } + read_guest_rom_state(s); + s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) - + le32_to_cpu(s->rom_state.vaddr); + + return 0; +} + +/* + * Tries to read the unique processor number from the Kernel Processor Control + * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR + * cannot be accessed or is considered invalid. This also ensures that we are + * not patching the wrong guest. + */ +static int get_kpcr_number(CPUX86State *env) +{ + struct kpcr { + uint8_t fill1[0x1c]; + uint32_t self; + uint8_t fill2[0x31]; + uint8_t number; + } QEMU_PACKED kpcr; + + if (cpu_memory_rw_debug(env, env->segs[R_FS].base, + (void *)&kpcr, sizeof(kpcr), 0) < 0 || + kpcr.self != env->segs[R_FS].base) { + return -1; + } + return kpcr.number; +} + +static int vapic_enable(VAPICROMState *s, CPUX86State *env) +{ + int cpu_number = get_kpcr_number(env); + target_phys_addr_t vapic_paddr; + static const uint8_t enabled = 1; + + if (cpu_number < 0) { + return -1; + } + vapic_paddr = s->vapic_paddr + + (((target_phys_addr_t)cpu_number) << VAPIC_CPU_SHIFT); + cpu_physical_memory_rw(vapic_paddr + offsetof(VAPICState, enabled), + (void *)&enabled, sizeof(enabled), 1); + apic_enable_vapic(env->apic_state, vapic_paddr); + + s->state = VAPIC_ACTIVE; + + return 0; +} + +static void patch_byte(CPUX86State *env, target_ulong addr, uint8_t byte) +{ + cpu_memory_rw_debug(env, addr, &byte, 1, 1); +} + +static void patch_call(VAPICROMState *s, CPUX86State *env, target_ulong ip, + uint32_t target) +{ + uint32_t offset; + + offset = cpu_to_le32(target - ip - 5); + patch_byte(env, ip, 0xe8); /* call near */ + cpu_memory_rw_debug(env, ip + 1, (void *)&offset, sizeof(offset), 1); +} + +static void patch_instruction(VAPICROMState *s, CPUX86State *env, target_ulong ip) +{ + target_phys_addr_t paddr; + VAPICHandlers *handlers; + uint8_t opcode[2]; + uint32_t imm32; + + if (smp_cpus == 1) { + handlers = &s->rom_state.up; + } else { + handlers = &s->rom_state.mp; + } + + pause_all_vcpus(); + + cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0); + + switch (opcode[0]) { + case 0x89: /* mov r32 to r/m32 */ + patch_byte(env, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ + patch_call(s, env, ip + 1, handlers->set_tpr); + break; + case 0x8b: /* mov r/m32 to r32 */ + patch_byte(env, ip, 0x90); + patch_call(s, env, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); + break; + case 0xa1: /* mov abs to eax */ + patch_call(s, env, ip, handlers->get_tpr[0]); + break; + case 0xa3: /* mov eax to abs */ + patch_call(s, env, ip, handlers->set_tpr_eax); + break; + case 0xc7: /* mov imm32, r/m32 (c7/0) */ + patch_byte(env, ip, 0x68); /* push imm32 */ + cpu_memory_rw_debug(env, ip + 6, (void *)&imm32, sizeof(imm32), 0); + cpu_memory_rw_debug(env, ip + 1, (void *)&imm32, sizeof(imm32), 1); + patch_call(s, env, ip + 5, handlers->set_tpr); + break; + case 0xff: /* push r/m32 */ + patch_byte(env, ip, 0x50); /* push eax */ + patch_call(s, env, ip + 1, handlers->get_tpr_stack); + break; + default: + abort(); + } + + resume_all_vcpus(); + + paddr = cpu_get_phys_page_debug(env, ip); + paddr += ip & ~TARGET_PAGE_MASK; + tb_invalidate_phys_page_range(paddr, paddr + 1, 1); +} + +void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, + TPRAccess access) +{ + VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev); + CPUX86State *env = cpu; + + cpu_synchronize_state(env); + + if (evaluate_tpr_instruction(s, env, &ip, access) < 0) { + if (s->state == VAPIC_ACTIVE) { + vapic_enable(s, env); + } + return; + } + if (update_rom_mapping(s, env, ip) < 0) { + return; + } + if (vapic_enable(s, env) < 0) { + return; + } + patch_instruction(s, env, ip); +} + +typedef struct VAPICEnableTPRReporting { + DeviceState *apic; + bool enable; +} VAPICEnableTPRReporting; + +static void vapic_do_enable_tpr_reporting(void *data) +{ + VAPICEnableTPRReporting *info = data; + + apic_enable_tpr_access_reporting(info->apic, info->enable); +} + +static void vapic_enable_tpr_reporting(bool enable) +{ + VAPICEnableTPRReporting info = { + .enable = enable, + }; + CPUX86State *env; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + info.apic = env->apic_state; + run_on_cpu(env, vapic_do_enable_tpr_reporting, &info); + } +} + +static void vapic_reset(DeviceState *dev) +{ + VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev); + + if (s->state == VAPIC_ACTIVE) { + s->state = VAPIC_STANDBY; + } + vapic_enable_tpr_reporting(false); +} + +/* + * Set the IRQ polling hypercalls to the supported variant: + * - vmcall if using KVM in-kernel irqchip + * - 32-bit VAPIC port write otherwise + */ +static int patch_hypercalls(VAPICROMState *s) +{ + target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; + static const uint8_t vmcall_pattern[] = { /* vmcall */ + 0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1 + }; + static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */ + 0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e + }; + uint8_t alternates[2]; + const uint8_t *pattern; + const uint8_t *patch; + int patches = 0; + off_t pos; + uint8_t *rom; + + rom = g_malloc(s->rom_size); + cpu_physical_memory_rw(rom_paddr, rom, s->rom_size, 0); + + for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) { + if (kvm_irqchip_in_kernel()) { + pattern = outl_pattern; + alternates[0] = outl_pattern[7]; + alternates[1] = outl_pattern[7]; + patch = &vmcall_pattern[5]; + } else { + pattern = vmcall_pattern; + alternates[0] = vmcall_pattern[7]; + alternates[1] = 0xd9; /* AMD's VMMCALL */ + patch = &outl_pattern[5]; + } + if (memcmp(rom + pos, pattern, 7) == 0 && + (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) { + cpu_physical_memory_rw(rom_paddr + pos + 5, (uint8_t *)patch, + 3, 1); + /* + * Don't flush the tb here. Under ordinary conditions, the patched + * calls are miles away from the current IP. Under malicious + * conditions, the guest could trick us to crash. + */ + } + } + + g_free(rom); + + if (patches != 0 && patches != 2) { + return -1; + } + + return 0; +} + +/* + * For TCG mode or the time KVM honors read-only memory regions, we need to + * enable write access to the option ROM so that variables can be updated by + * the guest. + */ +static void vapic_map_rom_writable(VAPICROMState *s) +{ + target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; + MemoryRegionSection section; + MemoryRegion *as; + size_t rom_size; + uint8_t *ram; + + as = sysbus_address_space(&s->busdev); + + if (s->rom_mapped_writable) { + memory_region_del_subregion(as, &s->rom); + memory_region_destroy(&s->rom); + } + + /* grab RAM memory region (region @rom_paddr may still be pc.rom) */ + section = memory_region_find(as, 0, 1); + + /* read ROM size from RAM region */ + ram = memory_region_get_ram_ptr(section.mr); + rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE; + s->rom_size = rom_size; + + /* We need to round to avoid creating subpages + * from which we cannot run code. */ + rom_size += rom_paddr & ~TARGET_PAGE_MASK; + rom_paddr &= TARGET_PAGE_MASK; + rom_size = TARGET_PAGE_ALIGN(rom_size); + + memory_region_init_alias(&s->rom, "kvmvapic-rom", section.mr, rom_paddr, + rom_size); + memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000); + s->rom_mapped_writable = true; +} + +static int vapic_prepare(VAPICROMState *s) +{ + vapic_map_rom_writable(s); + + if (patch_hypercalls(s) < 0) { + return -1; + } + + vapic_enable_tpr_reporting(true); + + return 0; +} + +static void vapic_write(void *opaque, target_phys_addr_t addr, uint64_t data, + unsigned int size) +{ + CPUX86State *env = cpu_single_env; + target_phys_addr_t rom_paddr; + VAPICROMState *s = opaque; + + cpu_synchronize_state(env); + + /* + * The VAPIC supports two PIO-based hypercalls, both via port 0x7E. + * o 16-bit write access: + * Reports the option ROM initialization to the hypervisor. Written + * value is the offset of the state structure in the ROM. + * o 8-bit write access: + * Reactivates the VAPIC after a guest hibernation, i.e. after the + * option ROM content has been re-initialized by a guest power cycle. + * o 32-bit write access: + * Poll for pending IRQs, considering the current VAPIC state. + */ + switch (size) { + case 2: + if (s->state == VAPIC_INACTIVE) { + rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK; + s->rom_state_paddr = rom_paddr + data; + + s->state = VAPIC_STANDBY; + } + if (vapic_prepare(s) < 0) { + s->state = VAPIC_INACTIVE; + break; + } + break; + case 1: + if (kvm_enabled()) { + /* + * Disable triggering instruction in ROM by writing a NOP. + * + * We cannot do this in TCG mode as the reported IP is not + * accurate. + */ + pause_all_vcpus(); + patch_byte(env, env->eip - 2, 0x66); + patch_byte(env, env->eip - 1, 0x90); + resume_all_vcpus(); + } + + if (s->state == VAPIC_ACTIVE) { + break; + } + if (update_rom_mapping(s, env, env->eip) < 0) { + break; + } + if (find_real_tpr_addr(s, env) < 0) { + break; + } + vapic_enable(s, env); + break; + default: + case 4: + if (!kvm_irqchip_in_kernel()) { + apic_poll_irq(env->apic_state); + } + break; + } +} + +static const MemoryRegionOps vapic_ops = { + .write = vapic_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int vapic_init(SysBusDevice *dev) +{ + VAPICROMState *s = FROM_SYSBUS(VAPICROMState, dev); + + memory_region_init_io(&s->io, &vapic_ops, s, "kvmvapic", 2); + sysbus_add_io(dev, VAPIC_IO_PORT, &s->io); + sysbus_init_ioports(dev, VAPIC_IO_PORT, 2); + + option_rom[nb_option_roms].name = "kvmvapic.bin"; + option_rom[nb_option_roms].bootindex = -1; + nb_option_roms++; + + return 0; +} + +static void do_vapic_enable(void *data) +{ + VAPICROMState *s = data; + + vapic_enable(s, first_cpu); +} + +static int vapic_post_load(void *opaque, int version_id) +{ + VAPICROMState *s = opaque; + uint8_t *zero; + + /* + * The old implementation of qemu-kvm did not provide the state + * VAPIC_STANDBY. Reconstruct it. + */ + if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) { + s->state = VAPIC_STANDBY; + } + + if (s->state != VAPIC_INACTIVE) { + if (vapic_prepare(s) < 0) { + return -1; + } + } + if (s->state == VAPIC_ACTIVE) { + if (smp_cpus == 1) { + run_on_cpu(first_cpu, do_vapic_enable, s); + } else { + zero = g_malloc0(s->rom_state.vapic_size); + cpu_physical_memory_rw(s->vapic_paddr, zero, + s->rom_state.vapic_size, 1); + g_free(zero); + } + } + + return 0; +} + +static const VMStateDescription vmstate_handlers = { + .name = "kvmvapic-handlers", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(set_tpr, VAPICHandlers), + VMSTATE_UINT32(set_tpr_eax, VAPICHandlers), + VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8), + VMSTATE_UINT32(get_tpr_stack, VAPICHandlers), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_guest_rom = { + .name = "kvmvapic-guest-rom", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UNUSED(8), /* signature */ + VMSTATE_UINT32(vaddr, GuestROMState), + VMSTATE_UINT32(fixup_start, GuestROMState), + VMSTATE_UINT32(fixup_end, GuestROMState), + VMSTATE_UINT32(vapic_vaddr, GuestROMState), + VMSTATE_UINT32(vapic_size, GuestROMState), + VMSTATE_UINT32(vcpu_shift, GuestROMState), + VMSTATE_UINT32(real_tpr_addr, GuestROMState), + VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers), + VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vapic = { + .name = "kvm-tpr-opt", /* compatible with qemu-kvm VAPIC */ + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = vapic_post_load, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom, + GuestROMState), + VMSTATE_UINT32(state, VAPICROMState), + VMSTATE_UINT32(real_tpr_addr, VAPICROMState), + VMSTATE_UINT32(rom_state_vaddr, VAPICROMState), + VMSTATE_UINT32(vapic_paddr, VAPICROMState), + VMSTATE_UINT32(rom_state_paddr, VAPICROMState), + VMSTATE_END_OF_LIST() + } +}; + +static void vapic_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->no_user = 1; + dc->reset = vapic_reset; + dc->vmsd = &vmstate_vapic; + sc->init = vapic_init; +} + +static TypeInfo vapic_type = { + .name = "kvmvapic", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VAPICROMState), + .class_init = vapic_class_init, +}; + +static void vapic_register(void) +{ + type_register_static(&vapic_type); +} + +type_init(vapic_register); diff --git a/hw/leon3.c b/hw/leon3.c index 71d79a65a7..0a5ff165a1 100644 --- a/hw/leon3.c +++ b/hw/leon3.c @@ -42,16 +42,16 @@ #define MAX_PILS 16 typedef struct ResetData { - CPUState *env; + CPUSPARCState *env; uint32_t entry; /* save kernel entry in case of reset */ } ResetData; static void main_cpu_reset(void *opaque) { ResetData *s = (ResetData *)opaque; - CPUState *env = s->env; + CPUSPARCState *env = s->env; - cpu_reset(env); + cpu_state_reset(env); env->halted = 0; env->pc = s->entry; @@ -65,7 +65,7 @@ void leon3_irq_ack(void *irq_manager, int intno) static void leon3_set_pil_in(void *opaque, uint32_t pil_in) { - CPUState *env = (CPUState *)opaque; + CPUSPARCState *env = (CPUSPARCState *)opaque; assert(env != NULL); @@ -101,7 +101,7 @@ static void leon3_generic_hw_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUSPARCState *env; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *prom = g_new(MemoryRegion, 1); diff --git a/hw/lm32_boards.c b/hw/lm32_boards.c index 3cdf120a14..4dd4f0ab90 100644 --- a/hw/lm32_boards.c +++ b/hw/lm32_boards.c @@ -31,7 +31,7 @@ #include "exec-memory.h" typedef struct { - CPUState *env; + CPULM32State *env; target_phys_addr_t bootstrap_pc; target_phys_addr_t flash_base; target_phys_addr_t hwsetup_base; @@ -42,7 +42,7 @@ typedef struct { static void cpu_irq_handler(void *opaque, int irq, int level) { - CPUState *env = opaque; + CPULM32State *env = opaque; if (level) { cpu_interrupt(env, CPU_INTERRUPT_HARD); @@ -54,9 +54,9 @@ static void cpu_irq_handler(void *opaque, int irq, int level) static void main_cpu_reset(void *opaque) { ResetInfo *reset_info = opaque; - CPUState *env = reset_info->env; + CPULM32State *env = reset_info->env; - cpu_reset(env); + cpu_state_reset(env); /* init defaults */ env->pc = (uint32_t)reset_info->bootstrap_pc; @@ -75,7 +75,7 @@ static void lm32_evr_init(ram_addr_t ram_size_not_used, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPULM32State *env; DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); @@ -163,7 +163,7 @@ static void lm32_uclinux_init(ram_addr_t ram_size_not_used, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPULM32State *env; DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index a46fdfc487..2b59c36ee6 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -24,11 +24,12 @@ #include "hw.h" #include "qemu-timer.h" #include "sysemu.h" -#include "pc.h" -#include "apic.h" -#include "isa.h" #include "mc146818rtc.h" +#ifdef TARGET_I386 +#include "apic.h" +#endif + //#define DEBUG_CMOS //#define DEBUG_COALESCED diff --git a/hw/mcf.h b/hw/mcf.h index baa790b0c5..19a8b54778 100644 --- a/hw/mcf.h +++ b/hw/mcf.h @@ -17,7 +17,7 @@ void mcf_uart_mm_init(struct MemoryRegion *sysmem, /* mcf_intc.c */ qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, target_phys_addr_t base, - CPUState *env); + CPUM68KState *env); /* mcf_fec.c */ void mcf_fec_init(struct MemoryRegion *sysmem, NICInfo *nd, @@ -25,6 +25,6 @@ void mcf_fec_init(struct MemoryRegion *sysmem, NICInfo *nd, /* mcf5206.c */ qemu_irq *mcf5206_init(struct MemoryRegion *sysmem, - uint32_t base, CPUState *env); + uint32_t base, CPUM68KState *env); #endif diff --git a/hw/mcf5206.c b/hw/mcf5206.c index 5110d833af..539b391338 100644 --- a/hw/mcf5206.c +++ b/hw/mcf5206.c @@ -145,7 +145,7 @@ static m5206_timer_state *m5206_timer_init(qemu_irq irq) /* System Integration Module. */ typedef struct { - CPUState *env; + CPUM68KState *env; MemoryRegion iomem; m5206_timer_state *timer[2]; void *uart[2]; @@ -525,7 +525,7 @@ static const MemoryRegionOps m5206_mbar_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, CPUState *env) +qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, CPUM68KState *env) { m5206_mbar_state *s; qemu_irq *pic; diff --git a/hw/mcf5208.c b/hw/mcf5208.c index aa11a755cc..d3ebe8d9ad 100644 --- a/hw/mcf5208.c +++ b/hw/mcf5208.c @@ -192,7 +192,7 @@ static void mcf5208evb_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUM68KState *env; int kernel_size; uint64_t elf_entry; target_phys_addr_t entry; diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c index 0b498dd3ac..cc1a5f3763 100644 --- a/hw/mcf_intc.c +++ b/hw/mcf_intc.c @@ -16,7 +16,7 @@ typedef struct { uint64_t ifr; uint64_t enabled; uint8_t icr[64]; - CPUState *env; + CPUM68KState *env; int active_vector; } mcf_intc_state; @@ -139,7 +139,7 @@ static const MemoryRegionOps mcf_intc_ops = { qemu_irq *mcf_intc_init(MemoryRegion *sysmem, target_phys_addr_t base, - CPUState *env) + CPUM68KState *env) { mcf_intc_state *s; diff --git a/hw/microblaze_boot.c b/hw/microblaze_boot.c new file mode 100644 index 0000000000..b4fbb10dd0 --- /dev/null +++ b/hw/microblaze_boot.c @@ -0,0 +1,177 @@ +/* + * Microblaze kernel loader + * + * Copyright (c) 2012 Peter Crosthwaite <peter.crosthwaite@petalogix.com> + * Copyright (c) 2012 PetaLogix + * Copyright (c) 2009 Edgar E. Iglesias. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-option.h" +#include "qemu-config.h" +#include "qemu-common.h" +#include "device_tree.h" +#include "loader.h" +#include "elf.h" + +#include "microblaze_boot.h" + +static struct +{ + void (*machine_cpu_reset)(CPUMBState *); + uint32_t bootstrap_pc; + uint32_t cmdline; + uint32_t fdt; +} boot_info; + +static void main_cpu_reset(void *opaque) +{ + CPUMBState *env = opaque; + + cpu_state_reset(env); + env->regs[5] = boot_info.cmdline; + env->regs[7] = boot_info.fdt; + env->sregs[SR_PC] = boot_info.bootstrap_pc; + if (boot_info.machine_cpu_reset) { + boot_info.machine_cpu_reset(env); + } +} + +static int microblaze_load_dtb(target_phys_addr_t addr, + uint32_t ramsize, + const char *kernel_cmdline, + const char *dtb_filename) +{ + int fdt_size; +#ifdef CONFIG_FDT + void *fdt = NULL; + int r; + + if (dtb_filename) { + fdt = load_device_tree(dtb_filename, &fdt_size); + } + if (!fdt) { + return 0; + } + + if (kernel_cmdline) { + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); + if (r < 0) { + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + } + } + + cpu_physical_memory_write(addr, (void *)fdt, fdt_size); +#else + /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob + to the kernel. */ + if (dtb_filename) { + fdt_size = load_image_targphys(dtb_filename, addr, 0x10000); + } + if (kernel_cmdline) { + fprintf(stderr, + "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); + } +#endif + return fdt_size; +} + +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return addr - 0x30000000LL; +} + +void microblaze_load_kernel(CPUMBState *env, target_phys_addr_t ddr_base, + uint32_t ramsize, const char *dtb_filename, + void (*machine_cpu_reset)(CPUMBState *)) +{ + + QemuOpts *machine_opts; + const char *kernel_filename = NULL; + const char *kernel_cmdline = NULL; + + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); + if (machine_opts) { + const char *dtb_arg; + kernel_filename = qemu_opt_get(machine_opts, "kernel"); + kernel_cmdline = qemu_opt_get(machine_opts, "append"); + dtb_arg = qemu_opt_get(machine_opts, "dtb"); + if (dtb_arg) { /* Preference a -dtb argument */ + dtb_filename = dtb_arg; + } else { /* default to pcbios dtb as passed by machine_init */ + dtb_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); + } + } + + boot_info.machine_cpu_reset = machine_cpu_reset; + qemu_register_reset(main_cpu_reset, env); + + if (kernel_filename) { + int kernel_size; + uint64_t entry, low, high; + uint32_t base32; + int big_endian = 0; + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#endif + + /* Boots a kernel elf binary. */ + kernel_size = load_elf(kernel_filename, NULL, NULL, + &entry, &low, &high, + big_endian, ELF_MACHINE, 0); + base32 = entry; + if (base32 == 0xc0000000) { + kernel_size = load_elf(kernel_filename, translate_kernel_address, + NULL, &entry, NULL, NULL, + big_endian, ELF_MACHINE, 0); + } + /* Always boot into physical ram. */ + boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); + + /* If it wasn't an ELF image, try an u-boot image. */ + if (kernel_size < 0) { + target_phys_addr_t uentry, loadaddr; + + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); + boot_info.bootstrap_pc = uentry; + high = (loadaddr + kernel_size + 3) & ~3; + } + + /* Not an ELF image nor an u-boot image, try a RAW image. */ + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, ddr_base, + ram_size); + boot_info.bootstrap_pc = ddr_base; + high = (ddr_base + kernel_size + 3) & ~3; + } + + boot_info.cmdline = high + 4096; + if (kernel_cmdline && strlen(kernel_cmdline)) { + pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); + } + /* Provide a device-tree. */ + boot_info.fdt = boot_info.cmdline + 4096; + microblaze_load_dtb(boot_info.fdt, ram_size, kernel_cmdline, + dtb_filename); + } + +} diff --git a/hw/microblaze_boot.h b/hw/microblaze_boot.h new file mode 100644 index 0000000000..bf9d136f12 --- /dev/null +++ b/hw/microblaze_boot.h @@ -0,0 +1,10 @@ +#ifndef __MICROBLAZE_BOOT__ +#define __MICROBLAZE_BOOT__ + +#include "hw.h" + +void microblaze_load_kernel(CPUMBState *env, target_phys_addr_t ddr_base, + uint32_t ramsize, const char *dtb_filename, + void (*machine_cpu_reset)(CPUMBState *)); + +#endif /* __MICROBLAZE_BOOT __ */ diff --git a/hw/microblaze_pic_cpu.c b/hw/microblaze_pic_cpu.c index 8b5623ce28..ff36a526fc 100644 --- a/hw/microblaze_pic_cpu.c +++ b/hw/microblaze_pic_cpu.c @@ -29,7 +29,7 @@ static void microblaze_pic_cpu_handler(void *opaque, int irq, int level) { - CPUState *env = (CPUState *)opaque; + CPUMBState *env = (CPUMBState *)opaque; int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD; if (level) @@ -38,7 +38,7 @@ static void microblaze_pic_cpu_handler(void *opaque, int irq, int level) cpu_reset_interrupt(env, type); } -qemu_irq *microblaze_pic_init_cpu(CPUState *env) +qemu_irq *microblaze_pic_init_cpu(CPUMBState *env) { return qemu_allocate_irqs(microblaze_pic_cpu_handler, env, 2); } diff --git a/hw/microblaze_pic_cpu.h b/hw/microblaze_pic_cpu.h index 4c76275976..43090a48ef 100644 --- a/hw/microblaze_pic_cpu.h +++ b/hw/microblaze_pic_cpu.h @@ -3,6 +3,6 @@ #include "qemu-common.h" -qemu_irq *microblaze_pic_init_cpu(CPUState *env); +qemu_irq *microblaze_pic_init_cpu(CPUMBState *env); #endif /* MICROBLAZE_PIC_CPU_H */ diff --git a/hw/milkymist.c b/hw/milkymist.c index eaef0c24c3..8bb6a97b22 100644 --- a/hw/milkymist.c +++ b/hw/milkymist.c @@ -37,7 +37,7 @@ #define KERNEL_LOAD_ADDR 0x40000000 typedef struct { - CPUState *env; + CPULM32State *env; target_phys_addr_t bootstrap_pc; target_phys_addr_t flash_base; target_phys_addr_t initrd_base; @@ -47,7 +47,7 @@ typedef struct { static void cpu_irq_handler(void *opaque, int irq, int level) { - CPUState *env = opaque; + CPULM32State *env = opaque; if (level) { cpu_interrupt(env, CPU_INTERRUPT_HARD); @@ -59,9 +59,9 @@ static void cpu_irq_handler(void *opaque, int irq, int level) static void main_cpu_reset(void *opaque) { ResetInfo *reset_info = opaque; - CPUState *env = reset_info->env; + CPULM32State *env = reset_info->env; - cpu_reset(env); + cpu_state_reset(env); /* init defaults */ env->pc = reset_info->bootstrap_pc; @@ -79,7 +79,7 @@ milkymist_init(ram_addr_t ram_size_not_used, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPULM32State *env; int kernel_size; DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); diff --git a/hw/mips_cpudevs.h b/hw/mips_cpudevs.h index db82b4105c..6bea24bf10 100644 --- a/hw/mips_cpudevs.h +++ b/hw/mips_cpudevs.h @@ -7,9 +7,9 @@ uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr); uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr); /* mips_int.c */ -void cpu_mips_irq_init_cpu(CPUState *env); +void cpu_mips_irq_init_cpu(CPUMIPSState *env); /* mips_timer.c */ -void cpu_mips_clock_init(CPUState *); +void cpu_mips_clock_init(CPUMIPSState *); #endif diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c index e3ba9dd42d..37dc711e08 100644 --- a/hw/mips_fulong2e.c +++ b/hw/mips_fulong2e.c @@ -29,7 +29,6 @@ #include "mips.h" #include "mips_cpudevs.h" #include "pci.h" -#include "usb-uhci.h" #include "qemu-char.h" #include "sysemu.h" #include "audio/audio.h" @@ -103,7 +102,7 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index, va_end(ap); } -static int64_t load_kernel (CPUState *env) +static int64_t load_kernel (CPUMIPSState *env) { int64_t kernel_entry, kernel_low, kernel_high; int index = 0; @@ -169,7 +168,7 @@ static int64_t load_kernel (CPUState *env) return kernel_entry; } -static void write_bootloader (CPUState *env, uint8_t *base, int64_t kernel_addr) +static void write_bootloader (CPUMIPSState *env, uint8_t *base, int64_t kernel_addr) { uint32_t *p; @@ -199,9 +198,9 @@ static void write_bootloader (CPUState *env, uint8_t *base, int64_t kernel_addr) static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUMIPSState *env = opaque; - cpu_reset(env); + cpu_state_reset(env); /* TODO: 2E reset stuff */ if (loaderparams.kernel_filename) { env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); @@ -249,7 +248,7 @@ static void network_init (void) static void cpu_request_exit(void *opaque, int irq, int level) { - CPUState *env = cpu_single_env; + CPUMIPSState *env = cpu_single_env; if (env && level) { cpu_exit(env); @@ -273,7 +272,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device, i2c_bus *smbus; int i; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - CPUState *env; + CPUMIPSState *env; /* init CPUs */ if (cpu_model == NULL) { @@ -355,8 +354,10 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device, isa_bus_irqs(isa_bus, i8259); vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1)); - usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2)); - usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3)); + pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2), + "vt82c686b-usb-uhci"); + pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3), + "vt82c686b-usb-uhci"); smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4), 0xeee1, NULL); diff --git a/hw/mips_int.c b/hw/mips_int.c index 477f6abf95..6423fd0bd9 100644 --- a/hw/mips_int.c +++ b/hw/mips_int.c @@ -26,7 +26,7 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) { - CPUState *env = (CPUState *)opaque; + CPUMIPSState *env = (CPUMIPSState *)opaque; if (irq < 0 || irq > 7) return; @@ -44,7 +44,7 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) } } -void cpu_mips_irq_init_cpu(CPUState *env) +void cpu_mips_irq_init_cpu(CPUMIPSState *env) { qemu_irq *qi; int i; @@ -55,7 +55,7 @@ void cpu_mips_irq_init_cpu(CPUState *env) } } -void cpu_mips_soft_irq(CPUState *env, int irq, int level) +void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level) { if (irq < 0 || irq > 2) { return; diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c index 2b4678e170..a6bc7badff 100644 --- a/hw/mips_jazz.c +++ b/hw/mips_jazz.c @@ -50,8 +50,8 @@ enum jazz_model_e static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; - cpu_reset(env); + CPUMIPSState *env = opaque; + cpu_state_reset(env); } static uint64_t rtc_read(void *opaque, target_phys_addr_t addr, unsigned size) @@ -97,7 +97,7 @@ static const MemoryRegionOps dma_dummy_ops = { static void cpu_request_exit(void *opaque, int irq, int level) { - CPUState *env = cpu_single_env; + CPUMIPSState *env = cpu_single_env; if (env && level) { cpu_exit(env); @@ -112,7 +112,7 @@ static void mips_jazz_init(MemoryRegion *address_space, { char *filename; int bios_size, n; - CPUState *env; + CPUMIPSState *env; qemu_irq *rc4030, *i8259; rc4030_dma *dmas; void* rc4030_opaque; diff --git a/hw/mips_malta.c b/hw/mips_malta.c index b1563ed2a7..4752bb2865 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -33,7 +33,6 @@ #include "mips.h" #include "mips_cpudevs.h" #include "pci.h" -#include "usb-uhci.h" #include "vmware_vga.h" #include "qemu-char.h" #include "sysemu.h" @@ -56,6 +55,13 @@ #define ENVP_NB_ENTRIES 16 #define ENVP_ENTRY_SIZE 256 +/* Hardware addresses */ +#define FLASH_ADDRESS 0x1e000000ULL +#define FPGA_ADDRESS 0x1f000000ULL +#define RESET_ADDRESS 0x1fc00000ULL + +#define FLASH_SIZE 0x400000 + #define MAX_IDE_BUS 2 typedef struct { @@ -332,9 +338,9 @@ static void malta_fpga_write(void *opaque, target_phys_addr_t addr, break; /* LEDBAR Register */ - /* XXX: implement a 8-LED array */ case 0x00408: s->leds = val & 0xff; + malta_fpga_update_display(s); break; /* ASCIIWORD Register */ @@ -501,7 +507,7 @@ static void network_init(void) a3 - RAM size in bytes */ -static void write_bootloader (CPUState *env, uint8_t *base, +static void write_bootloader (CPUMIPSState *env, uint8_t *base, int64_t kernel_entry) { uint32_t *p; @@ -737,7 +743,7 @@ static int64_t load_kernel (void) return kernel_entry; } -static void malta_mips_config(CPUState *env) +static void malta_mips_config(CPUMIPSState *env) { env->mvp->CP0_MVPConf0 |= ((smp_cpus - 1) << CP0MVPC0_PVPE) | ((smp_cpus * env->nr_threads - 1) << CP0MVPC0_PTC); @@ -745,8 +751,8 @@ static void malta_mips_config(CPUState *env) static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; - cpu_reset(env); + CPUMIPSState *env = opaque; + cpu_state_reset(env); /* The bootloader does not need to be rewritten as it is located in a read only location. The kernel location and the arguments table @@ -760,7 +766,7 @@ static void main_cpu_reset(void *opaque) static void cpu_request_exit(void *opaque, int irq, int level) { - CPUState *env = cpu_single_env; + CPUMIPSState *env = cpu_single_env; if (env && level) { cpu_exit(env); @@ -778,11 +784,11 @@ void mips_malta_init (ram_addr_t ram_size, MemoryRegion *system_memory = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios, *bios_alias = g_new(MemoryRegion, 1); - target_long bios_size; + target_long bios_size = FLASH_SIZE; int64_t kernel_entry; PCIBus *pci_bus; ISABus *isa_bus; - CPUState *env; + CPUMIPSState *env; qemu_irq *isa_irq; qemu_irq *cpu_exit_irq; int piix4_devfn; @@ -792,7 +798,7 @@ void mips_malta_init (ram_addr_t ram_size, DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *fd[MAX_FD]; int fl_idx = 0; - int fl_sectors = 0; + int fl_sectors = bios_size >> 16; int be; DeviceState *dev = qdev_create(NULL, "mips-malta"); @@ -848,19 +854,26 @@ void mips_malta_init (ram_addr_t ram_size, be = 0; #endif /* FPGA */ - malta_fpga_init(system_memory, 0x1f000000LL, env->irq[2], serial_hds[2]); + malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[2], serial_hds[2]); - /* Load firmware in flash / BIOS unless we boot directly into a kernel. */ + /* Load firmware in flash / BIOS. */ + dinfo = drive_get(IF_PFLASH, 0, fl_idx); +#ifdef DEBUG_BOARD_INIT + if (dinfo) { + printf("Register parallel flash %d size " TARGET_FMT_lx " at " + "addr %08llx '%s' %x\n", + fl_idx, bios_size, FLASH_ADDRESS, + bdrv_get_device_name(dinfo->bdrv), fl_sectors); + } +#endif + fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios", + BIOS_SIZE, dinfo ? dinfo->bdrv : NULL, + 65536, fl_sectors, + 4, 0x0000, 0x0000, 0x0000, 0x0000, be); + bios = pflash_cfi01_get_memory(fl); + fl_idx++; if (kernel_filename) { /* Write a small bootloader to the flash location. */ - bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, "mips_malta.bios", BIOS_SIZE); - vmstate_register_ram_global(bios); - memory_region_set_readonly(bios, true); - memory_region_init_alias(bios_alias, "bios.1fc", bios, 0, BIOS_SIZE); - /* Map the bios at two physical locations, as on the real board. */ - memory_region_add_subregion(system_memory, 0x1e000000LL, bios); - memory_region_add_subregion(system_memory, 0x1fc00000LL, bios_alias); loaderparams.ram_size = ram_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; @@ -868,45 +881,15 @@ void mips_malta_init (ram_addr_t ram_size, kernel_entry = load_kernel(); write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry); } else { - dinfo = drive_get(IF_PFLASH, 0, fl_idx); - if (dinfo) { - /* Load firmware from flash. */ - bios_size = 0x400000; - fl_sectors = bios_size >> 16; -#ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size " TARGET_FMT_lx " at " - "addr %08llx '%s' %x\n", - fl_idx, bios_size, 0x1e000000LL, - bdrv_get_device_name(dinfo->bdrv), fl_sectors); -#endif - fl = pflash_cfi01_register(0x1e000000LL, - NULL, "mips_malta.bios", BIOS_SIZE, - dinfo->bdrv, 65536, fl_sectors, - 4, 0x0000, 0x0000, 0x0000, 0x0000, be); - bios = pflash_cfi01_get_memory(fl); - /* Map the bios at two physical locations, as on the real board. */ - memory_region_init_alias(bios_alias, "bios.1fc", - bios, 0, BIOS_SIZE); - memory_region_add_subregion(system_memory, 0x1fc00000LL, - bios_alias); - fl_idx++; - } else { - bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, "mips_malta.bios", BIOS_SIZE); - vmstate_register_ram_global(bios); - memory_region_set_readonly(bios, true); - memory_region_init_alias(bios_alias, "bios.1fc", - bios, 0, BIOS_SIZE); - /* Map the bios at two physical locations, as on the real board. */ - memory_region_add_subregion(system_memory, 0x1e000000LL, bios); - memory_region_add_subregion(system_memory, 0x1fc00000LL, - bios_alias); + /* Load firmware from flash. */ + if (!dinfo) { /* Load a BIOS image. */ - if (bios_name == NULL) + if (bios_name == NULL) { bios_name = BIOS_FILENAME; + } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { - bios_size = load_image_targphys(filename, 0x1fc00000LL, + bios_size = load_image_targphys(filename, FLASH_ADDRESS, BIOS_SIZE); g_free(filename); } else { @@ -933,6 +916,10 @@ void mips_malta_init (ram_addr_t ram_size, #endif } + /* Map the BIOS at a 2nd physical location, as on the real board. */ + memory_region_init_alias(bios_alias, "bios.1fc", bios, 0, BIOS_SIZE); + memory_region_add_subregion(system_memory, RESET_ADDRESS, bios_alias); + /* Board ID = 0x420 (Malta Board with CoreLV) XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should map to the board ID. */ @@ -965,7 +952,7 @@ void mips_malta_init (ram_addr_t ram_size, isa_bus_irqs(isa_bus, s->i8259); pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1); - usb_uhci_piix4_init(pci_bus, piix4_devfn + 2); + pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci"); smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, isa_get_irq(NULL, 9), NULL, 0); /* TODO: Populate SPD eeprom data. */ diff --git a/hw/mips_mipssim.c b/hw/mips_mipssim.c index 76c95b2ec0..1ea7b58323 100644 --- a/hw/mips_mipssim.c +++ b/hw/mips_mipssim.c @@ -46,7 +46,7 @@ static struct _loaderparams { } loaderparams; typedef struct ResetData { - CPUState *env; + CPUMIPSState *env; uint64_t vector; } ResetData; @@ -105,9 +105,9 @@ static int64_t load_kernel(void) static void main_cpu_reset(void *opaque) { ResetData *s = (ResetData *)opaque; - CPUState *env = s->env; + CPUMIPSState *env = s->env; - cpu_reset(env); + cpu_state_reset(env); env->active_tc.PC = s->vector & ~(target_ulong)1; if (s->vector & 1) { env->hflags |= MIPS_HFLAG_M16; @@ -140,7 +140,7 @@ mips_mipssim_init (ram_addr_t ram_size, MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); - CPUState *env; + CPUMIPSState *env; ResetData *reset_info; int bios_size; diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c index 83401f0648..e2da49c09d 100644 --- a/hw/mips_r4k.c +++ b/hw/mips_r4k.c @@ -65,7 +65,7 @@ static const MemoryRegionOps mips_qemu_ops = { }; typedef struct ResetData { - CPUState *env; + CPUMIPSState *env; uint64_t vector; } ResetData; @@ -143,9 +143,9 @@ static int64_t load_kernel(void) static void main_cpu_reset(void *opaque) { ResetData *s = (ResetData *)opaque; - CPUState *env = s->env; + CPUMIPSState *env = s->env; - cpu_reset(env); + cpu_state_reset(env); env->active_tc.PC = s->vector; } @@ -162,7 +162,7 @@ void mips_r4k_init (ram_addr_t ram_size, MemoryRegion *bios; MemoryRegion *iomem = g_new(MemoryRegion, 1); int bios_size; - CPUState *env; + CPUMIPSState *env; ResetData *reset_info; int i; qemu_irq *i8259; diff --git a/hw/mips_timer.c b/hw/mips_timer.c index cf6ac694e3..7aa9004a0e 100644 --- a/hw/mips_timer.c +++ b/hw/mips_timer.c @@ -27,7 +27,7 @@ #define TIMER_FREQ 100 * 1000 * 1000 /* XXX: do not use a global */ -uint32_t cpu_mips_get_random (CPUState *env) +uint32_t cpu_mips_get_random (CPUMIPSState *env) { static uint32_t lfsr = 1; static uint32_t prev_idx = 0; @@ -42,7 +42,7 @@ uint32_t cpu_mips_get_random (CPUState *env) } /* MIPS R4K timer */ -static void cpu_mips_timer_update(CPUState *env) +static void cpu_mips_timer_update(CPUMIPSState *env) { uint64_t now, next; uint32_t wait; @@ -55,7 +55,7 @@ static void cpu_mips_timer_update(CPUState *env) } /* Expire the timer. */ -static void cpu_mips_timer_expire(CPUState *env) +static void cpu_mips_timer_expire(CPUMIPSState *env) { cpu_mips_timer_update(env); if (env->insn_flags & ISA_MIPS32R2) { @@ -64,7 +64,7 @@ static void cpu_mips_timer_expire(CPUState *env) qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); } -uint32_t cpu_mips_get_count (CPUState *env) +uint32_t cpu_mips_get_count (CPUMIPSState *env) { if (env->CP0_Cause & (1 << CP0Ca_DC)) { return env->CP0_Count; @@ -83,7 +83,7 @@ uint32_t cpu_mips_get_count (CPUState *env) } } -void cpu_mips_store_count (CPUState *env, uint32_t count) +void cpu_mips_store_count (CPUMIPSState *env, uint32_t count) { if (env->CP0_Cause & (1 << CP0Ca_DC)) env->CP0_Count = count; @@ -97,7 +97,7 @@ void cpu_mips_store_count (CPUState *env, uint32_t count) } } -void cpu_mips_store_compare (CPUState *env, uint32_t value) +void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value) { env->CP0_Compare = value; if (!(env->CP0_Cause & (1 << CP0Ca_DC))) @@ -107,12 +107,12 @@ void cpu_mips_store_compare (CPUState *env, uint32_t value) qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); } -void cpu_mips_start_count(CPUState *env) +void cpu_mips_start_count(CPUMIPSState *env) { cpu_mips_store_count(env, env->CP0_Count); } -void cpu_mips_stop_count(CPUState *env) +void cpu_mips_stop_count(CPUMIPSState *env) { /* Store the current value */ env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock_ns(vm_clock), @@ -121,7 +121,7 @@ void cpu_mips_stop_count(CPUState *env) static void mips_timer_cb (void *opaque) { - CPUState *env; + CPUMIPSState *env; env = opaque; #if 0 @@ -139,7 +139,7 @@ static void mips_timer_cb (void *opaque) env->CP0_Count--; } -void cpu_mips_clock_init (CPUState *env) +void cpu_mips_clock_init (CPUMIPSState *env) { env->timer = qemu_new_timer_ns(vm_clock, &mips_timer_cb, env); env->CP0_Compare = 0; diff --git a/hw/mipsnet.c b/hw/mipsnet.c index 50d92f8f54..31072463f4 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -224,7 +224,7 @@ static NetClientInfo net_mipsnet_info = { .cleanup = mipsnet_cleanup, }; -static MemoryRegionOps mipsnet_ioport_ops = { +static const MemoryRegionOps mipsnet_ioport_ops = { .read = mipsnet_ioport_read, .write = mipsnet_ioport_write, .impl.min_access_size = 1, diff --git a/hw/mpc8544_guts.c b/hw/mpc8544_guts.c index aeb2de7ccc..13b0dddc1e 100644 --- a/hw/mpc8544_guts.c +++ b/hw/mpc8544_guts.c @@ -62,7 +62,7 @@ static uint64_t mpc8544_guts_read(void *opaque, target_phys_addr_t addr, unsigned size) { uint32_t value = 0; - CPUState *env = cpu_single_env; + CPUPPCState *env = cpu_single_env; addr &= MPC8544_GUTS_MMIO_SIZE - 1; switch (addr) { diff --git a/hw/musicpal.c b/hw/musicpal.c index 187a1aef5e..c9f845a3f2 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -1513,7 +1513,7 @@ static void musicpal_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUARMState *env; qemu_irq *cpu_pic; qemu_irq pic[32]; DeviceState *dev; diff --git a/hw/nseries.c b/hw/nseries.c index c5b31843dd..a5cfa8ccbc 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -45,7 +45,6 @@ struct n800_s { uint32_t (*txrx)(void *opaque, uint32_t value, int len); uWireSlave *chip; } ts; - i2c_bus *i2c; int keymap[0x80]; DeviceState *kbd; @@ -194,12 +193,10 @@ static void n8x0_i2c_setup(struct n800_s *s) { DeviceState *dev; qemu_irq tmp_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TMP105_GPIO); - - /* Attach the CPU on one end of our I2C bus. */ - s->i2c = omap_i2c_bus(s->cpu->i2c[0]); + i2c_bus *i2c = omap_i2c_bus(s->cpu->i2c[0]); /* Attach a menelaus PM chip */ - dev = i2c_create_slave(s->i2c, "twl92230", N8X0_MENELAUS_ADDR); + dev = i2c_create_slave(i2c, "twl92230", N8X0_MENELAUS_ADDR); qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(s->cpu->ih[0], OMAP_INT_24XX_SYS_NIRQ)); @@ -207,7 +204,7 @@ static void n8x0_i2c_setup(struct n800_s *s) qemu_system_powerdown = qdev_get_gpio_in(dev, 3); /* Attach a TMP105 PM chip (A0 wired to ground) */ - dev = i2c_create_slave(s->i2c, "tmp105", N8X0_TMP105_ADDR); + dev = i2c_create_slave(i2c, "tmp105", N8X0_TMP105_ADDR); qdev_connect_gpio_out(dev, 0, tmp_irq); } @@ -391,7 +388,8 @@ static void n810_kbd_setup(struct n800_s *s) /* Attach the LM8322 keyboard to the I2C bus, * should happen in n8x0_i2c_setup and s->kbd be initialised here. */ - s->kbd = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR); + s->kbd = i2c_create_slave(omap_i2c_bus(s->cpu->i2c[0]), + "lm8323", N810_LM8323_ADDR); qdev_connect_gpio_out(s->kbd, 0, kbd_irq); } diff --git a/hw/omap.h b/hw/omap.h index 60fa34cef2..6c3d004719 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -764,16 +764,7 @@ void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover); void omap_mmc_enable(struct omap_mmc_s *s, int enable); /* omap_i2c.c */ -struct omap_i2c_s; -struct omap_i2c_s *omap_i2c_init(MemoryRegion *sysmem, - target_phys_addr_t base, - qemu_irq irq, - qemu_irq *dma, - omap_clk clk); -struct omap_i2c_s *omap2_i2c_init(struct omap_target_agent_s *ta, - qemu_irq irq, qemu_irq *dma, omap_clk fclk, omap_clk iclk); -void omap_i2c_reset(struct omap_i2c_s *s); -i2c_bus *omap_i2c_bus(struct omap_i2c_s *s); +i2c_bus *omap_i2c_bus(DeviceState *omap_i2c); # define cpu_is_omap310(cpu) (cpu->mpu_model == omap310) # define cpu_is_omap1510(cpu) (cpu->mpu_model == omap1510) @@ -813,7 +804,7 @@ struct omap_mpu_state_s { omap3630, } mpu_model; - CPUState *env; + CPUARMState *env; qemu_irq *drq; @@ -867,7 +858,7 @@ struct omap_mpu_state_s { struct omap_pwl_s *pwl; struct omap_pwt_s *pwt; - struct omap_i2c_s *i2c[2]; + DeviceState *i2c[2]; struct omap_rtc_s *rtc; diff --git a/hw/omap1.c b/hw/omap1.c index 1aa5f2388b..2a341bfe7f 100644 --- a/hw/omap1.c +++ b/hw/omap1.c @@ -3694,7 +3694,6 @@ static void omap1_mpu_reset(void *opaque) omap_uwire_reset(mpu->microwire); omap_pwl_reset(mpu->pwl); omap_pwt_reset(mpu->pwt); - omap_i2c_reset(mpu->i2c[0]); omap_rtc_reset(mpu->rtc); omap_mcbsp_reset(mpu->mcbsp1); omap_mcbsp_reset(mpu->mcbsp2); @@ -3702,7 +3701,7 @@ static void omap1_mpu_reset(void *opaque) omap_lpg_reset(mpu->led[0]); omap_lpg_reset(mpu->led[1]); omap_clkm_reset(mpu); - cpu_reset(mpu->env); + cpu_state_reset(mpu->env); } static const struct omap_map_s { @@ -3993,9 +3992,15 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, s->pwt = omap_pwt_init(system_memory, 0xfffb6000, omap_findclk(s, "armxor_ck")); - s->i2c[0] = omap_i2c_init(system_memory, 0xfffb3800, - qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C), - &s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck")); + s->i2c[0] = qdev_create(NULL, "omap_i2c"); + qdev_prop_set_uint8(s->i2c[0], "revision", 0x11); + qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "mpuper_ck")); + qdev_init_nofail(s->i2c[0]); + busdev = sysbus_from_qdev(s->i2c[0]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C)); + sysbus_connect_irq(busdev, 1, s->drq[OMAP_DMA_I2C_TX]); + sysbus_connect_irq(busdev, 2, s->drq[OMAP_DMA_I2C_RX]); + sysbus_mmio_map(busdev, 0, 0xfffb3800); s->rtc = omap_rtc_init(system_memory, 0xfffb4800, qdev_get_gpio_in(s->ih[1], OMAP_INT_RTC_TIMER), diff --git a/hw/omap2.c b/hw/omap2.c index a6851b0fb0..42fce5e986 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -2222,9 +2222,7 @@ static void omap2_mpu_reset(void *opaque) omap_mmc_reset(mpu->mmc); omap_mcspi_reset(mpu->mcspi[0]); omap_mcspi_reset(mpu->mcspi[1]); - omap_i2c_reset(mpu->i2c[0]); - omap_i2c_reset(mpu->i2c[1]); - cpu_reset(mpu->env); + cpu_state_reset(mpu->env); } static int omap2_validate_addr(struct omap_mpu_state_s *s, @@ -2395,16 +2393,29 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, omap_findclk(s, "clk32-kHz"), omap_findclk(s, "core_l4_iclk")); - s->i2c[0] = omap2_i2c_init(omap_l4tao(s->l4, 5), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ), - &s->drq[OMAP24XX_DMA_I2C1_TX], - omap_findclk(s, "i2c1.fclk"), - omap_findclk(s, "i2c1.iclk")); - s->i2c[1] = omap2_i2c_init(omap_l4tao(s->l4, 6), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ), - &s->drq[OMAP24XX_DMA_I2C2_TX], - omap_findclk(s, "i2c2.fclk"), - omap_findclk(s, "i2c2.iclk")); + s->i2c[0] = qdev_create(NULL, "omap_i2c"); + qdev_prop_set_uint8(s->i2c[0], "revision", 0x34); + qdev_prop_set_ptr(s->i2c[0], "iclk", omap_findclk(s, "i2c1.iclk")); + qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "i2c1.fclk")); + qdev_init_nofail(s->i2c[0]); + busdev = sysbus_from_qdev(s->i2c[0]); + sysbus_connect_irq(busdev, 0, + qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ)); + sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C1_TX]); + sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C1_RX]); + sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 5), 0)); + + s->i2c[1] = qdev_create(NULL, "omap_i2c"); + qdev_prop_set_uint8(s->i2c[1], "revision", 0x34); + qdev_prop_set_ptr(s->i2c[1], "iclk", omap_findclk(s, "i2c2.iclk")); + qdev_prop_set_ptr(s->i2c[1], "fclk", omap_findclk(s, "i2c2.fclk")); + qdev_init_nofail(s->i2c[1]); + busdev = sysbus_from_qdev(s->i2c[1]); + sysbus_connect_irq(busdev, 0, + qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ)); + sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C2_TX]); + sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C2_RX]); + sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 6), 0)); s->gpio = qdev_create(NULL, "omap2-gpio"); qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c index 5ec422c566..20bc82e3b8 100644 --- a/hw/omap_i2c.c +++ b/hw/omap_i2c.c @@ -19,14 +19,20 @@ #include "hw.h" #include "i2c.h" #include "omap.h" +#include "sysbus.h" -struct omap_i2c_s { + +typedef struct OMAPI2CState { + SysBusDevice busdev; MemoryRegion iomem; qemu_irq irq; qemu_irq drq[2]; i2c_bus *bus; uint8_t revision; + void *iclk; + void *fclk; + uint8_t mask; uint16_t stat; uint16_t dma; @@ -40,12 +46,12 @@ struct omap_i2c_s { uint8_t divider; uint8_t times[2]; uint16_t test; -}; +} OMAPI2CState; #define OMAP2_INTR_REV 0x34 #define OMAP2_GC_REV 0x34 -static void omap_i2c_interrupts_update(struct omap_i2c_s *s) +static void omap_i2c_interrupts_update(OMAPI2CState *s) { qemu_set_irq(s->irq, s->stat & s->mask); if ((s->dma >> 15) & 1) /* RDMA_EN */ @@ -54,7 +60,7 @@ static void omap_i2c_interrupts_update(struct omap_i2c_s *s) qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ } -static void omap_i2c_fifo_run(struct omap_i2c_s *s) +static void omap_i2c_fifo_run(OMAPI2CState *s) { int ack = 1; @@ -122,8 +128,10 @@ static void omap_i2c_fifo_run(struct omap_i2c_s *s) s->control &= ~(1 << 1); /* STP */ } -void omap_i2c_reset(struct omap_i2c_s *s) +static void omap_i2c_reset(DeviceState *dev) { + OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, + sysbus_from_qdev(dev)); s->mask = 0; s->stat = 0; s->dma = 0; @@ -143,7 +151,7 @@ void omap_i2c_reset(struct omap_i2c_s *s) static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr) { - struct omap_i2c_s *s = (struct omap_i2c_s *) opaque; + OMAPI2CState *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; uint16_t ret; @@ -243,7 +251,7 @@ static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr) static void omap_i2c_write(void *opaque, target_phys_addr_t addr, uint32_t value) { - struct omap_i2c_s *s = (struct omap_i2c_s *) opaque; + OMAPI2CState *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; int nack; @@ -309,14 +317,14 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr, } if (value & 2) - omap_i2c_reset(s); + omap_i2c_reset(&s->busdev.qdev); break; case 0x24: /* I2C_CON */ s->control = value & 0xcf87; if (~value & (1 << 15)) { /* I2C_EN */ if (s->revision < OMAP2_INTR_REV) - omap_i2c_reset(s); + omap_i2c_reset(&s->busdev.qdev); break; } if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ @@ -385,7 +393,7 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr, static void omap_i2c_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) { - struct omap_i2c_s *s = (struct omap_i2c_s *) opaque; + OMAPI2CState *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; switch (offset) { @@ -426,50 +434,59 @@ static const MemoryRegionOps omap_i2c_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -struct omap_i2c_s *omap_i2c_init(MemoryRegion *sysmem, - target_phys_addr_t base, - qemu_irq irq, - qemu_irq *dma, - omap_clk clk) +static int omap_i2c_init(SysBusDevice *dev) { - struct omap_i2c_s *s = (struct omap_i2c_s *) - g_malloc0(sizeof(struct omap_i2c_s)); - - /* TODO: set a value greater or equal to real hardware */ - s->revision = 0x11; - s->irq = irq; - s->drq[0] = dma[0]; - s->drq[1] = dma[1]; - s->bus = i2c_init_bus(NULL, "i2c"); - omap_i2c_reset(s); + OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, dev); - memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap.i2c", 0x800); - memory_region_add_subregion(sysmem, base, &s->iomem); - - return s; + if (!s->fclk) { + hw_error("omap_i2c: fclk not connected\n"); + } + if (s->revision >= OMAP2_INTR_REV && !s->iclk) { + /* Note that OMAP1 doesn't have a separate interface clock */ + hw_error("omap_i2c: iclk not connected\n"); + } + sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(dev, &s->drq[0]); + sysbus_init_irq(dev, &s->drq[1]); + memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap.i2c", + (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000); + sysbus_init_mmio(dev, &s->iomem); + s->bus = i2c_init_bus(&dev->qdev, NULL); + return 0; } -struct omap_i2c_s *omap2_i2c_init(struct omap_target_agent_s *ta, - qemu_irq irq, qemu_irq *dma, omap_clk fclk, omap_clk iclk) -{ - struct omap_i2c_s *s = (struct omap_i2c_s *) - g_malloc0(sizeof(struct omap_i2c_s)); +static Property omap_i2c_properties[] = { + DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), + DEFINE_PROP_PTR("iclk", OMAPI2CState, iclk), + DEFINE_PROP_PTR("fclk", OMAPI2CState, fclk), + DEFINE_PROP_END_OF_LIST(), +}; - s->revision = 0x34; - s->irq = irq; - s->drq[0] = dma[0]; - s->drq[1] = dma[1]; - s->bus = i2c_init_bus(NULL, "i2c"); - omap_i2c_reset(s); +static void omap_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = omap_i2c_init; + dc->props = omap_i2c_properties; + dc->reset = omap_i2c_reset; +} - memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap2.i2c", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); +static TypeInfo omap_i2c_info = { + .name = "omap_i2c", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OMAPI2CState), + .class_init = omap_i2c_class_init, +}; - return s; +static void omap_i2c_register_types(void) +{ + type_register_static(&omap_i2c_info); } -i2c_bus *omap_i2c_bus(struct omap_i2c_s *s) +i2c_bus *omap_i2c_bus(DeviceState *omap_i2c) { + OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, sysbus_from_qdev(omap_i2c)); return s->bus; } + +type_init(omap_i2c_register_types) diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c index 9b036cb103..4c7696935f 100644 --- a/hw/opencores_eth.c +++ b/hw/opencores_eth.c @@ -692,12 +692,12 @@ static void open_eth_desc_write(void *opaque, } -static MemoryRegionOps open_eth_reg_ops = { +static const MemoryRegionOps open_eth_reg_ops = { .read = open_eth_reg_read, .write = open_eth_reg_write, }; -static MemoryRegionOps open_eth_desc_ops = { +static const MemoryRegionOps open_eth_desc_ops = { .read = open_eth_desc_read, .write = open_eth_desc_write, }; diff --git a/hw/openpic.c b/hw/openpic.c index 280b7a9bbb..58ef871f68 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -713,7 +713,7 @@ static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val) DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); if (addr & 0xF) return; - addr -= 0x1100; + addr -= 0x10; addr &= 0xFFFF; idx = (addr & 0xFFF0) >> 6; addr = addr & 0x30; @@ -746,7 +746,7 @@ static uint32_t openpic_timer_read (void *opaque, uint32_t addr) retval = 0xFFFFFFFF; if (addr & 0xF) return retval; - addr -= 0x1100; + addr -= 0x10; addr &= 0xFFFF; idx = (addr & 0xFFF0) >> 6; addr = addr & 0x30; @@ -1361,7 +1361,6 @@ static void mpic_src_ext_write (void *opaque, target_phys_addr_t addr, if (addr & 0xF) return; - addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_EXT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1385,7 +1384,6 @@ static uint32_t mpic_src_ext_read (void *opaque, target_phys_addr_t addr) if (addr & 0xF) return retval; - addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_EXT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1411,7 +1409,6 @@ static void mpic_src_int_write (void *opaque, target_phys_addr_t addr, if (addr & 0xF) return; - addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_INT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1435,7 +1432,6 @@ static uint32_t mpic_src_int_read (void *opaque, target_phys_addr_t addr) if (addr & 0xF) return retval; - addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_INT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1461,7 +1457,6 @@ static void mpic_src_msg_write (void *opaque, target_phys_addr_t addr, if (addr & 0xF) return; - addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSG_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1485,7 +1480,6 @@ static uint32_t mpic_src_msg_read (void *opaque, target_phys_addr_t addr) if (addr & 0xF) return retval; - addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSG_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1511,7 +1505,6 @@ static void mpic_src_msi_write (void *opaque, target_phys_addr_t addr, if (addr & 0xF) return; - addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSI_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1534,7 +1527,6 @@ static uint32_t mpic_src_msi_read (void *opaque, target_phys_addr_t addr) if (addr & 0xF) return retval; - addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSI_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { diff --git a/hw/pc.c b/hw/pc.c index 59a7f3928f..83a1b5b32b 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -140,7 +140,7 @@ void cpu_smm_register(cpu_set_smm_t callback, void *arg) smm_arg = arg; } -void cpu_smm_update(CPUState *env) +void cpu_smm_update(CPUX86State *env) { if (smm_set && smm_arg && env == first_cpu) smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg); @@ -148,7 +148,7 @@ void cpu_smm_update(CPUState *env) /* IRQ handling */ -int cpu_get_pic_interrupt(CPUState *env) +int cpu_get_pic_interrupt(CPUX86State *env) { int intno; @@ -167,7 +167,7 @@ int cpu_get_pic_interrupt(CPUState *env) static void pic_irq_request(void *opaque, int irq, int level) { - CPUState *env = first_cpu; + CPUX86State *env = first_cpu; DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq); if (env->apic_state) { @@ -335,6 +335,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, { int val, nb, nb_heads, max_track, last_sect, i; FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE }; + FDriveRate rate; BlockDriverState *fd[MAX_FD]; static pc_cmos_init_late_arg arg; @@ -383,7 +384,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, if (fd[i] && bdrv_is_inserted(fd[i])) { bdrv_get_floppy_geometry_hint(fd[i], &nb_heads, &max_track, &last_sect, FDRIVE_DRV_NONE, - &fd_type[i]); + &fd_type[i], &rate); } } } @@ -521,7 +522,7 @@ type_init(port92_register_types) static void handle_a20_line_change(void *opaque, int irq, int level) { - CPUState *cpu = opaque; + CPUX86State *cpu = opaque; /* XXX: send to all CPUs ? */ /* XXX: add logic to handle multiple A20 line sources */ @@ -868,7 +869,7 @@ void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd) nb_ne2k++; } -int cpu_is_bsp(CPUState *env) +int cpu_is_bsp(CPUX86State *env) { /* We hard-wire the BSP to the first CPU. */ return env->cpu_index == 0; @@ -916,7 +917,7 @@ static DeviceState *apic_init(void *env, uint8_t apic_id) void pc_acpi_smi_interrupt(void *opaque, int irq, int level) { - CPUState *s = opaque; + CPUX86State *s = opaque; if (level) { cpu_interrupt(s, CPU_INTERRUPT_SMI); @@ -925,15 +926,15 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) static void pc_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUX86State *env = opaque; - cpu_reset(env); + cpu_state_reset(env); env->halted = !cpu_is_bsp(env); } -static CPUState *pc_new_cpu(const char *cpu_model) +static CPUX86State *pc_new_cpu(const char *cpu_model) { - CPUState *env; + CPUX86State *env; env = cpu_init(cpu_model); if (!env) { @@ -1069,7 +1070,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) static void cpu_request_exit(void *opaque, int irq, int level) { - CPUState *env = cpu_single_env; + CPUX86State *env = cpu_single_env; if (env && level) { cpu_exit(env); @@ -1095,7 +1096,13 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); - if (!no_hpet) { + /* + * Check if an HPET shall be created. + * + * Without KVM_CAP_PIT_STATE2, we cannot switch off the in-kernel PIT + * when the HPET wants to take over. Thus we have to disable the latter. + */ + if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) { hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); if (hpet) { @@ -1111,7 +1118,11 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, qemu_register_boot_set(pc_boot_set, *rtc_state); - pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq); + if (kvm_irqchip_in_kernel()) { + pit = kvm_pit_init(isa_bus, 0x40); + } else { + pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq); + } if (hpet) { /* connect PIT to output control line of the HPET */ qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(&pit->qdev, 0)); diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 5e11d15026..3f99f9a7c2 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -28,8 +28,6 @@ #include "pc.h" #include "apic.h" #include "pci.h" -#include "usb-uhci.h" -#include "usb-ohci.h" #include "net.h" #include "boards.h" #include "ide.h" @@ -284,7 +282,7 @@ static void pc_init1(MemoryRegion *system_memory, floppy, idebus[0], idebus[1], rtc_state); if (pci_enabled && usb_enabled) { - usb_uhci_piix3_init(pci_bus, piix3_devfn + 2); + pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); } if (pci_enabled && acpi_enabled) { @@ -384,6 +382,10 @@ static QEMUMachine pc_machine_v1_0 = { .driver = "pc-sysfw", .property = "rom_only", .value = stringify(1), + }, { + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", }, { /* end of list */ } }, @@ -399,6 +401,10 @@ static QEMUMachine pc_machine_v0_15 = { .driver = "pc-sysfw", .property = "rom_only", .value = stringify(1), + }, { + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", }, { /* end of list */ } }, @@ -434,6 +440,10 @@ static QEMUMachine pc_machine_v0_14 = { .driver = "virtio-balloon-pci", .property = "event_idx", .value = "off", + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", }, { .driver = "pc-sysfw", @@ -486,6 +496,10 @@ static QEMUMachine pc_machine_v0_13 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", }, { .driver = "pc-sysfw", @@ -542,6 +556,10 @@ static QEMUMachine pc_machine_v0_12 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", }, { .driver = "pc-sysfw", @@ -606,6 +624,10 @@ static QEMUMachine pc_machine_v0_11 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", }, { .driver = "pc-sysfw", @@ -682,6 +704,10 @@ static QEMUMachine pc_machine_v0_10 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", }, { .driver = "pc-sysfw", diff --git a/hw/pci.c b/hw/pci.c index bf046bfcad..ed8ec99073 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -841,7 +841,6 @@ static int pci_unregister_device(DeviceState *dev) pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); - g_free(pci_dev->romfile); do_pci_unregister_device(pci_dev); return 0; } diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c index 98978f89b6..31a434872d 100644 --- a/hw/petalogix_ml605_mmu.c +++ b/hw/petalogix_ml605_mmu.c @@ -32,35 +32,30 @@ #include "sysemu.h" #include "devices.h" #include "boards.h" -#include "device_tree.h" #include "xilinx.h" -#include "loader.h" -#include "elf.h" #include "blockdev.h" #include "pc.h" #include "exec-memory.h" +#include "microblaze_boot.h" #include "microblaze_pic_cpu.h" #include "xilinx_axidma.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (32 * 1024 * 1024) -static struct -{ - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t fdt; -} boot_info; +#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" -static void main_cpu_reset(void *opaque) -{ - CPUState *env = opaque; +#define MEMORY_BASEADDR 0x50000000 +#define FLASH_BASEADDR 0x86000000 +#define INTC_BASEADDR 0x81800000 +#define TIMER_BASEADDR 0x83c00000 +#define UART16550_BASEADDR 0x83e00000 +#define AXIENET_BASEADDR 0x82780000 +#define AXIDMA_BASEADDR 0x84600000 - cpu_reset(env); - env->regs[5] = boot_info.cmdline; - env->regs[7] = boot_info.fdt; - env->sregs[SR_PC] = boot_info.bootstrap_pc; +static void machine_cpu_reset(CPUMBState *env) +{ env->pvr.regs[10] = 0x0e000000; /* virtex 6 */ /* setup pvr to match kernel setting */ env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK; @@ -71,70 +66,6 @@ static void main_cpu_reset(void *opaque) env->pvr.regs[5] = 0xc56be000; } -#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" -static int petalogix_load_device_tree(target_phys_addr_t addr, - uint32_t ramsize, - target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) -{ - char *path; - int fdt_size; -#ifdef CONFIG_FDT - void *fdt; - int r; - - /* Try the local "mb.dtb" override. */ - fdt = load_device_tree("mb.dtb", &fdt_size); - if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt = load_device_tree(path, &fdt_size); - g_free(path); - } - if (!fdt) { - return 0; - } - } - - r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); - if (r < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - } - cpu_physical_memory_write(addr, (void *)fdt, fdt_size); -#else - /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob - to the kernel. */ - fdt_size = load_image_targphys("mb.dtb", addr, 0x10000); - if (fdt_size < 0) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt_size = load_image_targphys(path, addr, 0x10000); - g_free(path); - } - } - - if (kernel_cmdline) { - fprintf(stderr, - "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); - } -#endif - return fdt_size; -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0x30000000LL; -} - -#define MEMORY_BASEADDR 0x50000000 -#define FLASH_BASEADDR 0x86000000 -#define INTC_BASEADDR 0x81800000 -#define TIMER_BASEADDR 0x83c00000 -#define UART16550_BASEADDR 0x83e00000 -#define AXIENET_BASEADDR 0x82780000 -#define AXIDMA_BASEADDR 0x84600000 - static void petalogix_ml605_init(ram_addr_t ram_size, const char *boot_device, @@ -144,8 +75,7 @@ petalogix_ml605_init(ram_addr_t ram_size, { MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev; - CPUState *env; - int kernel_size; + CPUMBState *env; DriveInfo *dinfo; int i; target_phys_addr_t ddr_base = MEMORY_BASEADDR; @@ -159,8 +89,6 @@ petalogix_ml605_init(ram_addr_t ram_size, } env = cpu_init(cpu_model); - qemu_register_reset(main_cpu_reset, env); - /* Attach emulated BRAM through the LMB. */ memory_region_init_ram(phys_lmb_bram, "petalogix_ml605.lmb_bram", LMB_BRAM_SIZE); @@ -203,55 +131,9 @@ petalogix_ml605_init(ram_addr_t ram_size, irq[1], irq[0], 100 * 1000000); } - if (kernel_filename) { - uint64_t entry, low, high; - uint32_t base32; - int big_endian = 0; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#endif + microblaze_load_kernel(env, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE, + machine_cpu_reset); - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, - big_endian, ELF_MACHINE, 0); - base32 = entry; - if (base32 == 0xc0000000) { - kernel_size = load_elf(kernel_filename, translate_kernel_address, - NULL, &entry, NULL, NULL, - big_endian, ELF_MACHINE, 0); - } - /* Always boot into physical ram. */ - boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); - - /* If it wasn't an ELF image, try an u-boot image. */ - if (kernel_size < 0) { - target_phys_addr_t uentry, loadaddr; - - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); - boot_info.bootstrap_pc = uentry; - high = (loadaddr + kernel_size + 3) & ~3; - } - - /* Not an ELF image nor an u-boot image, try a RAW image. */ - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ddr_base, - ram_size); - boot_info.bootstrap_pc = ddr_base; - high = (ddr_base + kernel_size + 3) & ~3; - } - - boot_info.cmdline = high + 4096; - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); - } - /* Provide a device-tree. */ - boot_info.fdt = boot_info.cmdline + 4096; - petalogix_load_device_tree(boot_info.fdt, ram_size, - 0, 0, - kernel_cmdline); - } } static QEMUMachine petalogix_ml605_machine = { diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c index d448a41f68..ff154c7aa9 100644 --- a/hw/petalogix_s3adsp1800_mmu.c +++ b/hw/petalogix_s3adsp1800_mmu.c @@ -30,87 +30,29 @@ #include "sysemu.h" #include "devices.h" #include "boards.h" -#include "device_tree.h" #include "xilinx.h" -#include "loader.h" -#include "elf.h" #include "blockdev.h" #include "exec-memory.h" +#include "microblaze_boot.h" #include "microblaze_pic_cpu.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (16 * 1024 * 1024) -static struct -{ - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t fdt; -} boot_info; - -static void main_cpu_reset(void *opaque) -{ - CPUState *env = opaque; - - cpu_reset(env); - env->regs[5] = boot_info.cmdline; - env->regs[7] = boot_info.fdt; - env->sregs[SR_PC] = boot_info.bootstrap_pc; -} - #define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb" -static int petalogix_load_device_tree(target_phys_addr_t addr, - uint32_t ramsize, - target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) -{ - char *path; - int fdt_size; -#ifdef CONFIG_FDT - void *fdt; - int r; - - /* Try the local "mb.dtb" override. */ - fdt = load_device_tree("mb.dtb", &fdt_size); - if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt = load_device_tree(path, &fdt_size); - g_free(path); - } - if (!fdt) - return 0; - } - r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); - if (r < 0) - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - cpu_physical_memory_write (addr, (void *)fdt, fdt_size); -#else - /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob - to the kernel. */ - fdt_size = load_image_targphys("mb.dtb", addr, 0x10000); - if (fdt_size < 0) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt_size = load_image_targphys(path, addr, 0x10000); - g_free(path); - } - } +#define MEMORY_BASEADDR 0x90000000 +#define FLASH_BASEADDR 0xa0000000 +#define INTC_BASEADDR 0x81800000 +#define TIMER_BASEADDR 0x83c00000 +#define UARTLITE_BASEADDR 0x84000000 +#define ETHLITE_BASEADDR 0x81000000 - if (kernel_cmdline) { - fprintf(stderr, - "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); - } -#endif - return fdt_size; -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +static void machine_cpu_reset(CPUMBState *env) { - return addr - 0x30000000LL; + /* FIXME: move to machine specfic cpu reset */ + env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */ } static void @@ -121,11 +63,10 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { DeviceState *dev; - CPUState *env; - int kernel_size; + CPUMBState *env; DriveInfo *dinfo; int i; - target_phys_addr_t ddr_base = 0x90000000; + target_phys_addr_t ddr_base = MEMORY_BASEADDR; MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32], *cpu_irq; @@ -137,9 +78,6 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size, } env = cpu_init(cpu_model); - env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */ - qemu_register_reset(main_cpu_reset, env); - /* Attach emulated BRAM through the LMB. */ memory_region_init_ram(phys_lmb_bram, "petalogix_s3adsp1800.lmb_bram", LMB_BRAM_SIZE); @@ -151,72 +89,25 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size, memory_region_add_subregion(sysmem, ddr_base, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(0xa0000000, + pflash_cfi01_register(FLASH_BASEADDR, NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, (64 * 1024), FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = microblaze_pic_init_cpu(env); - dev = xilinx_intc_create(0x81800000, cpu_irq[0], 2); + dev = xilinx_intc_create(INTC_BASEADDR, cpu_irq[0], 2); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } - sysbus_create_simple("xilinx,uartlite", 0x84000000, irq[3]); + sysbus_create_simple("xilinx,uartlite", UARTLITE_BASEADDR, irq[3]); /* 2 timers at irq 2 @ 62 Mhz. */ - xilinx_timer_create(0x83c00000, irq[0], 2, 62 * 1000000); - xilinx_ethlite_create(&nd_table[0], 0x81000000, irq[1], 0, 0); - - if (kernel_filename) { - uint64_t entry, low, high; - uint32_t base32; - int big_endian = 0; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#endif - - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, - big_endian, ELF_MACHINE, 0); - base32 = entry; - if (base32 == 0xc0000000) { - kernel_size = load_elf(kernel_filename, translate_kernel_address, - NULL, &entry, NULL, NULL, - big_endian, ELF_MACHINE, 0); - } - /* Always boot into physical ram. */ - boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); - - /* If it wasn't an ELF image, try an u-boot image. */ - if (kernel_size < 0) { - target_phys_addr_t uentry, loadaddr; - - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); - boot_info.bootstrap_pc = uentry; - high = (loadaddr + kernel_size + 3) & ~3; - } - - /* Not an ELF image nor an u-boot image, try a RAW image. */ - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ddr_base, - ram_size); - boot_info.bootstrap_pc = ddr_base; - high = (ddr_base + kernel_size + 3) & ~3; - } - - boot_info.cmdline = high + 4096; - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); - } - /* Provide a device-tree. */ - boot_info.fdt = boot_info.cmdline + 4096; - petalogix_load_device_tree(boot_info.fdt, ram_size, - 0, 0, - kernel_cmdline); - } + xilinx_timer_create(TIMER_BASEADDR, irq[0], 2, 62 * 1000000); + xilinx_ethlite_create(&nd_table[0], ETHLITE_BASEADDR, irq[1], 0, 0); + + microblaze_load_kernel(env, ddr_base, ram_size, + BINARY_DEVICE_TREE_FILE, machine_cpu_reset); } static QEMUMachine petalogix_s3adsp1800_machine = { diff --git a/hw/ppc.c b/hw/ppc.c index 59882e2ecd..98546de991 100644 --- a/hw/ppc.c +++ b/hw/ppc.c @@ -47,10 +47,10 @@ # define LOG_TB(...) do { } while (0) #endif -static void cpu_ppc_tb_stop (CPUState *env); -static void cpu_ppc_tb_start (CPUState *env); +static void cpu_ppc_tb_stop (CPUPPCState *env); +static void cpu_ppc_tb_start (CPUPPCState *env); -void ppc_set_irq(CPUState *env, int n_IRQ, int level) +void ppc_set_irq(CPUPPCState *env, int n_IRQ, int level) { unsigned int old_pending = env->pending_interrupts; @@ -77,7 +77,7 @@ void ppc_set_irq(CPUState *env, int n_IRQ, int level) /* PowerPC 6xx / 7xx internal IRQ controller */ static void ppc6xx_set_irq (void *opaque, int pin, int level) { - CPUState *env = opaque; + CPUPPCState *env = opaque; int cur_level; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, @@ -131,13 +131,7 @@ static void ppc6xx_set_irq (void *opaque, int pin, int level) /* Level sensitive - active low */ if (level) { LOG_IRQ("%s: reset the CPU\n", __func__); - env->interrupt_request |= CPU_INTERRUPT_EXITTB; - /* XXX: TOFIX */ -#if 0 - cpu_reset(env); -#else - qemu_system_reset_request(); -#endif + cpu_interrupt(env, CPU_INTERRUPT_RESET); } break; case PPC6xx_INPUT_SRESET: @@ -157,7 +151,7 @@ static void ppc6xx_set_irq (void *opaque, int pin, int level) } } -void ppc6xx_irq_init (CPUState *env) +void ppc6xx_irq_init (CPUPPCState *env) { env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, env, PPC6xx_INPUT_NB); @@ -167,7 +161,7 @@ void ppc6xx_irq_init (CPUState *env) /* PowerPC 970 internal IRQ controller */ static void ppc970_set_irq (void *opaque, int pin, int level) { - CPUState *env = opaque; + CPUPPCState *env = opaque; int cur_level; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, @@ -214,10 +208,7 @@ static void ppc970_set_irq (void *opaque, int pin, int level) case PPC970_INPUT_HRESET: /* Level sensitive - active low */ if (level) { -#if 0 // XXX: TOFIX - LOG_IRQ("%s: reset the CPU\n", __func__); - cpu_reset(env); -#endif + cpu_interrupt(env, CPU_INTERRUPT_RESET); } break; case PPC970_INPUT_SRESET: @@ -242,7 +233,7 @@ static void ppc970_set_irq (void *opaque, int pin, int level) } } -void ppc970_irq_init (CPUState *env) +void ppc970_irq_init (CPUPPCState *env) { env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, env, PPC970_INPUT_NB); @@ -251,7 +242,7 @@ void ppc970_irq_init (CPUState *env) /* POWER7 internal IRQ controller */ static void power7_set_irq (void *opaque, int pin, int level) { - CPUState *env = opaque; + CPUPPCState *env = opaque; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, env, pin, level); @@ -275,7 +266,7 @@ static void power7_set_irq (void *opaque, int pin, int level) } } -void ppcPOWER7_irq_init (CPUState *env) +void ppcPOWER7_irq_init (CPUPPCState *env) { env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, env, POWER7_INPUT_NB); @@ -285,7 +276,7 @@ void ppcPOWER7_irq_init (CPUState *env) /* PowerPC 40x internal IRQ controller */ static void ppc40x_set_irq (void *opaque, int pin, int level) { - CPUState *env = opaque; + CPUPPCState *env = opaque; int cur_level; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, @@ -355,7 +346,7 @@ static void ppc40x_set_irq (void *opaque, int pin, int level) } } -void ppc40x_irq_init (CPUState *env) +void ppc40x_irq_init (CPUPPCState *env) { env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq, env, PPC40x_INPUT_NB); @@ -364,7 +355,7 @@ void ppc40x_irq_init (CPUState *env) /* PowerPC E500 internal IRQ controller */ static void ppce500_set_irq (void *opaque, int pin, int level) { - CPUState *env = opaque; + CPUPPCState *env = opaque; int cur_level; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, @@ -416,7 +407,7 @@ static void ppce500_set_irq (void *opaque, int pin, int level) } } -void ppce500_irq_init (CPUState *env) +void ppce500_irq_init (CPUPPCState *env) { env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, env, PPCE500_INPUT_NB); @@ -430,7 +421,7 @@ uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset; } -uint64_t cpu_ppc_load_tbl (CPUState *env) +uint64_t cpu_ppc_load_tbl (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -445,7 +436,7 @@ uint64_t cpu_ppc_load_tbl (CPUState *env) return tb; } -static inline uint32_t _cpu_ppc_load_tbu(CPUState *env) +static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -456,7 +447,7 @@ static inline uint32_t _cpu_ppc_load_tbu(CPUState *env) return tb >> 32; } -uint32_t cpu_ppc_load_tbu (CPUState *env) +uint32_t cpu_ppc_load_tbu (CPUPPCState *env) { if (kvm_enabled()) { return env->spr[SPR_TBU]; @@ -473,7 +464,7 @@ static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, __func__, value, *tb_offsetp); } -void cpu_ppc_store_tbl (CPUState *env, uint32_t value) +void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -484,7 +475,7 @@ void cpu_ppc_store_tbl (CPUState *env, uint32_t value) &tb_env->tb_offset, tb | (uint64_t)value); } -static inline void _cpu_ppc_store_tbu(CPUState *env, uint32_t value) +static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -495,12 +486,12 @@ static inline void _cpu_ppc_store_tbu(CPUState *env, uint32_t value) &tb_env->tb_offset, ((uint64_t)value << 32) | tb); } -void cpu_ppc_store_tbu (CPUState *env, uint32_t value) +void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value) { _cpu_ppc_store_tbu(env, value); } -uint64_t cpu_ppc_load_atbl (CPUState *env) +uint64_t cpu_ppc_load_atbl (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -511,7 +502,7 @@ uint64_t cpu_ppc_load_atbl (CPUState *env) return tb; } -uint32_t cpu_ppc_load_atbu (CPUState *env) +uint32_t cpu_ppc_load_atbu (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -522,7 +513,7 @@ uint32_t cpu_ppc_load_atbu (CPUState *env) return tb >> 32; } -void cpu_ppc_store_atbl (CPUState *env, uint32_t value) +void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -533,7 +524,7 @@ void cpu_ppc_store_atbl (CPUState *env, uint32_t value) &tb_env->atb_offset, tb | (uint64_t)value); } -void cpu_ppc_store_atbu (CPUState *env, uint32_t value) +void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -544,7 +535,7 @@ void cpu_ppc_store_atbu (CPUState *env, uint32_t value) &tb_env->atb_offset, ((uint64_t)value << 32) | tb); } -static void cpu_ppc_tb_stop (CPUState *env) +static void cpu_ppc_tb_stop (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb, atb, vmclk; @@ -566,7 +557,7 @@ static void cpu_ppc_tb_stop (CPUState *env) } } -static void cpu_ppc_tb_start (CPUState *env) +static void cpu_ppc_tb_start (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb, atb, vmclk; @@ -587,7 +578,7 @@ static void cpu_ppc_tb_start (CPUState *env) } } -static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next) +static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) { ppc_tb_t *tb_env = env->tb_env; uint32_t decr; @@ -606,7 +597,7 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next) return decr; } -uint32_t cpu_ppc_load_decr (CPUState *env) +uint32_t cpu_ppc_load_decr (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; @@ -617,14 +608,14 @@ uint32_t cpu_ppc_load_decr (CPUState *env) return _cpu_ppc_load_decr(env, tb_env->decr_next); } -uint32_t cpu_ppc_load_hdecr (CPUState *env) +uint32_t cpu_ppc_load_hdecr (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; return _cpu_ppc_load_decr(env, tb_env->hdecr_next); } -uint64_t cpu_ppc_load_purr (CPUState *env) +uint64_t cpu_ppc_load_purr (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t diff; @@ -637,23 +628,23 @@ uint64_t cpu_ppc_load_purr (CPUState *env) /* When decrementer expires, * all we need to do is generate or queue a CPU exception */ -static inline void cpu_ppc_decr_excp(CPUState *env) +static inline void cpu_ppc_decr_excp(CPUPPCState *env) { /* Raise it */ LOG_TB("raise decrementer exception\n"); ppc_set_irq(env, PPC_INTERRUPT_DECR, 1); } -static inline void cpu_ppc_hdecr_excp(CPUState *env) +static inline void cpu_ppc_hdecr_excp(CPUPPCState *env) { /* Raise it */ LOG_TB("raise decrementer exception\n"); ppc_set_irq(env, PPC_INTERRUPT_HDECR, 1); } -static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, +static void __cpu_ppc_store_decr (CPUPPCState *env, uint64_t *nextp, struct QEMUTimer *timer, - void (*raise_excp)(CPUState *), + void (*raise_excp)(CPUPPCState *), uint32_t decr, uint32_t value, int is_excp) { @@ -690,7 +681,7 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, } } -static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr, +static inline void _cpu_ppc_store_decr(CPUPPCState *env, uint32_t decr, uint32_t value, int is_excp) { ppc_tb_t *tb_env = env->tb_env; @@ -699,7 +690,7 @@ static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr, &cpu_ppc_decr_excp, decr, value, is_excp); } -void cpu_ppc_store_decr (CPUState *env, uint32_t value) +void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value) { _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0); } @@ -709,7 +700,7 @@ static void cpu_ppc_decr_cb (void *opaque) _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1); } -static inline void _cpu_ppc_store_hdecr(CPUState *env, uint32_t hdecr, +static inline void _cpu_ppc_store_hdecr(CPUPPCState *env, uint32_t hdecr, uint32_t value, int is_excp) { ppc_tb_t *tb_env = env->tb_env; @@ -720,7 +711,7 @@ static inline void _cpu_ppc_store_hdecr(CPUState *env, uint32_t hdecr, } } -void cpu_ppc_store_hdecr (CPUState *env, uint32_t value) +void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value) { _cpu_ppc_store_hdecr(env, cpu_ppc_load_hdecr(env), value, 0); } @@ -730,7 +721,7 @@ static void cpu_ppc_hdecr_cb (void *opaque) _cpu_ppc_store_hdecr(opaque, 0x00000000, 0xFFFFFFFF, 1); } -void cpu_ppc_store_purr (CPUState *env, uint64_t value) +void cpu_ppc_store_purr (CPUPPCState *env, uint64_t value) { ppc_tb_t *tb_env = env->tb_env; @@ -740,7 +731,7 @@ void cpu_ppc_store_purr (CPUState *env, uint64_t value) static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) { - CPUState *env = opaque; + CPUPPCState *env = opaque; ppc_tb_t *tb_env = env->tb_env; tb_env->tb_freq = freq; @@ -755,7 +746,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) } /* Set up (once) timebase frequency (in Hz) */ -clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq) +clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) { ppc_tb_t *tb_env; @@ -778,28 +769,28 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq) /* Specific helpers for POWER & PowerPC 601 RTC */ #if 0 -static clk_setup_cb cpu_ppc601_rtc_init (CPUState *env) +static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env) { return cpu_ppc_tb_init(env, 7812500); } #endif -void cpu_ppc601_store_rtcu (CPUState *env, uint32_t value) +void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value) { _cpu_ppc_store_tbu(env, value); } -uint32_t cpu_ppc601_load_rtcu (CPUState *env) +uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env) { return _cpu_ppc_load_tbu(env); } -void cpu_ppc601_store_rtcl (CPUState *env, uint32_t value) +void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value) { cpu_ppc_store_tbl(env, value & 0x3FFFFF80); } -uint32_t cpu_ppc601_load_rtcl (CPUState *env) +uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env) { return cpu_ppc_load_tbl(env) & 0x3FFFFF80; } @@ -823,7 +814,7 @@ struct ppc40x_timer_t { /* Fixed interval timer */ static void cpu_4xx_fit_cb (void *opaque) { - CPUState *env; + CPUPPCState *env; ppc_tb_t *tb_env; ppc40x_timer_t *ppc40x_timer; uint64_t now, next; @@ -862,7 +853,7 @@ static void cpu_4xx_fit_cb (void *opaque) } /* Programmable interval timer */ -static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) +static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) { ppc40x_timer_t *ppc40x_timer; uint64_t now, next; @@ -891,7 +882,7 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) static void cpu_4xx_pit_cb (void *opaque) { - CPUState *env; + CPUPPCState *env; ppc_tb_t *tb_env; ppc40x_timer_t *ppc40x_timer; @@ -913,7 +904,7 @@ static void cpu_4xx_pit_cb (void *opaque) /* Watchdog timer */ static void cpu_4xx_wdt_cb (void *opaque) { - CPUState *env; + CPUPPCState *env; ppc_tb_t *tb_env; ppc40x_timer_t *ppc40x_timer; uint64_t now, next; @@ -978,7 +969,7 @@ static void cpu_4xx_wdt_cb (void *opaque) } } -void store_40x_pit (CPUState *env, target_ulong val) +void store_40x_pit (CPUPPCState *env, target_ulong val) { ppc_tb_t *tb_env; ppc40x_timer_t *ppc40x_timer; @@ -990,14 +981,14 @@ void store_40x_pit (CPUState *env, target_ulong val) start_stop_pit(env, tb_env, 0); } -target_ulong load_40x_pit (CPUState *env) +target_ulong load_40x_pit (CPUPPCState *env) { return cpu_ppc_load_decr(env); } static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) { - CPUState *env = opaque; + CPUPPCState *env = opaque; ppc_tb_t *tb_env = env->tb_env; LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__, @@ -1007,7 +998,7 @@ static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) /* XXX: we should also update all timers */ } -clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, +clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, unsigned int decr_excp) { ppc_tb_t *tb_env; @@ -1093,7 +1084,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) return -1; } -int ppc_dcr_register (CPUState *env, int dcrn, void *opaque, +int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque, dcr_read_cb dcr_read, dcr_write_cb dcr_write) { ppc_dcr_t *dcr_env; @@ -1116,7 +1107,7 @@ int ppc_dcr_register (CPUState *env, int dcrn, void *opaque, return 0; } -int ppc_dcr_init (CPUState *env, int (*read_error)(int dcrn), +int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn), int (*write_error)(int dcrn)) { ppc_dcr_t *dcr_env; diff --git a/hw/ppc.h b/hw/ppc.h index 9f911704af..2f3ea277bc 100644 --- a/hw/ppc.h +++ b/hw/ppc.h @@ -1,4 +1,4 @@ -void ppc_set_irq (CPUState *env, int n_IRQ, int level); +void ppc_set_irq (CPUPPCState *env, int n_IRQ, int level); /* PowerPC hardware exceptions management helpers */ typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); @@ -43,32 +43,32 @@ struct ppc_tb_t { */ uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); -clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq); +clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq); /* Embedded PowerPC DCR management */ typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn); typedef void (*dcr_write_cb)(void *opaque, int dcrn, uint32_t val); -int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn), +int ppc_dcr_init (CPUPPCState *env, int (*dcr_read_error)(int dcrn), int (*dcr_write_error)(int dcrn)); -int ppc_dcr_register (CPUState *env, int dcrn, void *opaque, +int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque, dcr_read_cb drc_read, dcr_write_cb dcr_write); -clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, +clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, unsigned int decr_excp); /* Embedded PowerPC reset */ -void ppc40x_core_reset (CPUState *env); -void ppc40x_chip_reset (CPUState *env); -void ppc40x_system_reset (CPUState *env); +void ppc40x_core_reset (CPUPPCState *env); +void ppc40x_chip_reset (CPUPPCState *env); +void ppc40x_system_reset (CPUPPCState *env); void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val); extern CPUWriteMemoryFunc * const PPC_io_write[]; extern CPUReadMemoryFunc * const PPC_io_read[]; void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val); -void ppc40x_irq_init (CPUState *env); -void ppce500_irq_init (CPUState *env); -void ppc6xx_irq_init (CPUState *env); -void ppc970_irq_init (CPUState *env); -void ppcPOWER7_irq_init (CPUState *env); +void ppc40x_irq_init (CPUPPCState *env); +void ppce500_irq_init (CPUPPCState *env); +void ppc6xx_irq_init (CPUPPCState *env); +void ppc970_irq_init (CPUPPCState *env); +void ppcPOWER7_irq_init (CPUPPCState *env); /* PPC machines for OpenBIOS */ enum { @@ -89,4 +89,4 @@ enum { #define PPC_SERIAL_MM_BAUDBASE 399193 /* ppc_booke.c */ -void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags); +void ppc_booke_timers_init(CPUPPCState *env, uint32_t freq, uint32_t flags); diff --git a/hw/ppc405.h b/hw/ppc405.h index d8fdf0930a..1f5dc5fd36 100644 --- a/hw/ppc405.h +++ b/hw/ppc405.h @@ -56,23 +56,23 @@ struct ppc4xx_bd_info_t { }; /* PowerPC 405 core */ -ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd, +ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd, uint32_t flags); -CPUState *ppc405cr_init(MemoryRegion *address_space_mem, +CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, MemoryRegion ram_memories[4], target_phys_addr_t ram_bases[4], target_phys_addr_t ram_sizes[4], uint32_t sysclk, qemu_irq **picp, int do_init); -CPUState *ppc405ep_init(MemoryRegion *address_space_mem, +CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, MemoryRegion ram_memories[2], target_phys_addr_t ram_bases[2], target_phys_addr_t ram_sizes[2], uint32_t sysclk, qemu_irq **picp, int do_init); /* IBM STBxxx microcontrollers */ -CPUState *ppc_stb025_init (MemoryRegion ram_memories[2], +CPUPPCState *ppc_stb025_init (MemoryRegion ram_memories[2], target_phys_addr_t ram_bases[2], target_phys_addr_t ram_sizes[2], uint32_t sysclk, qemu_irq **picp, diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c index 98079fa23f..89e5013b57 100644 --- a/hw/ppc405_uc.c +++ b/hw/ppc405_uc.c @@ -41,7 +41,7 @@ #define DEBUG_CLOCKS //#define DEBUG_CLOCKS_LL -ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd, +ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd, uint32_t flags) { ram_addr_t bdloc; @@ -169,7 +169,7 @@ static void ppc4xx_plb_reset (void *opaque) plb->besr = 0x00000000; } -static void ppc4xx_plb_init(CPUState *env) +static void ppc4xx_plb_init(CPUPPCState *env) { ppc4xx_plb_t *plb; @@ -245,7 +245,7 @@ static void ppc4xx_pob_reset (void *opaque) pob->besr[1] = 0x0000000; } -static void ppc4xx_pob_init(CPUState *env) +static void ppc4xx_pob_init(CPUPPCState *env) { ppc4xx_pob_t *pob; @@ -574,7 +574,7 @@ static void ebc_reset (void *opaque) ebc->cfg = 0x80400000; } -static void ppc405_ebc_init(CPUState *env) +static void ppc405_ebc_init(CPUPPCState *env) { ppc4xx_ebc_t *ebc; @@ -657,7 +657,7 @@ static void ppc405_dma_reset (void *opaque) dma->pol = 0x00000000; } -static void ppc405_dma_init(CPUState *env, qemu_irq irqs[4]) +static void ppc405_dma_init(CPUPPCState *env, qemu_irq irqs[4]) { ppc405_dma_t *dma; @@ -960,7 +960,7 @@ static void ocm_reset (void *opaque) ocm->dsacntl = dsacntl; } -static void ppc405_ocm_init(CPUState *env) +static void ppc405_ocm_init(CPUPPCState *env) { ppc405_ocm_t *ocm; @@ -1713,7 +1713,7 @@ static void ppc40x_mal_reset (void *opaque) mal->txeobisr = 0x00000000; } -static void ppc405_mal_init(CPUState *env, qemu_irq irqs[4]) +static void ppc405_mal_init(CPUPPCState *env, qemu_irq irqs[4]) { ppc40x_mal_t *mal; int i; @@ -1764,36 +1764,24 @@ static void ppc405_mal_init(CPUState *env, qemu_irq irqs[4]) /*****************************************************************************/ /* SPR */ -void ppc40x_core_reset (CPUState *env) +void ppc40x_core_reset (CPUPPCState *env) { target_ulong dbsr; printf("Reset PowerPC core\n"); - env->interrupt_request |= CPU_INTERRUPT_EXITTB; - /* XXX: TOFIX */ -#if 0 - cpu_reset(env); -#else - qemu_system_reset_request(); -#endif + cpu_interrupt(env, CPU_INTERRUPT_RESET); dbsr = env->spr[SPR_40x_DBSR]; dbsr &= ~0x00000300; dbsr |= 0x00000100; env->spr[SPR_40x_DBSR] = dbsr; } -void ppc40x_chip_reset (CPUState *env) +void ppc40x_chip_reset (CPUPPCState *env) { target_ulong dbsr; printf("Reset PowerPC chip\n"); - env->interrupt_request |= CPU_INTERRUPT_EXITTB; - /* XXX: TOFIX */ -#if 0 - cpu_reset(env); -#else - qemu_system_reset_request(); -#endif + cpu_interrupt(env, CPU_INTERRUPT_RESET); /* XXX: TODO reset all internal peripherals */ dbsr = env->spr[SPR_40x_DBSR]; dbsr &= ~0x00000300; @@ -1801,13 +1789,13 @@ void ppc40x_chip_reset (CPUState *env) env->spr[SPR_40x_DBSR] = dbsr; } -void ppc40x_system_reset (CPUState *env) +void ppc40x_system_reset (CPUPPCState *env) { printf("Reset PowerPC system\n"); qemu_system_reset_request(); } -void store_40x_dbcr0 (CPUState *env, uint32_t val) +void store_40x_dbcr0 (CPUPPCState *env, uint32_t val) { switch ((val >> 28) & 0x3) { case 0x0: @@ -2078,7 +2066,7 @@ static void ppc405cr_clk_init (ppc405cr_cpc_t *cpc) cpc->psr |= D << 17; } -static void ppc405cr_cpc_init (CPUState *env, clk_setup_t clk_setup[7], +static void ppc405cr_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[7], uint32_t sysclk) { ppc405cr_cpc_t *cpc; @@ -2108,7 +2096,7 @@ static void ppc405cr_cpc_init (CPUState *env, clk_setup_t clk_setup[7], qemu_register_reset(ppc405cr_cpc_reset, cpc); } -CPUState *ppc405cr_init(MemoryRegion *address_space_mem, +CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, MemoryRegion ram_memories[4], target_phys_addr_t ram_bases[4], target_phys_addr_t ram_sizes[4], @@ -2117,7 +2105,7 @@ CPUState *ppc405cr_init(MemoryRegion *address_space_mem, { clk_setup_t clk_setup[PPC405CR_CLK_NB]; qemu_irq dma_irqs[4]; - CPUState *env; + CPUPPCState *env; qemu_irq *pic, *irqs; memset(clk_setup, 0, sizeof(clk_setup)); @@ -2420,7 +2408,7 @@ static void ppc405ep_cpc_reset (void *opaque) } /* XXX: sysclk should be between 25 and 100 MHz */ -static void ppc405ep_cpc_init (CPUState *env, clk_setup_t clk_setup[8], +static void ppc405ep_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[8], uint32_t sysclk) { ppc405ep_cpc_t *cpc; @@ -2457,7 +2445,7 @@ static void ppc405ep_cpc_init (CPUState *env, clk_setup_t clk_setup[8], #endif } -CPUState *ppc405ep_init(MemoryRegion *address_space_mem, +CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, MemoryRegion ram_memories[2], target_phys_addr_t ram_bases[2], target_phys_addr_t ram_sizes[2], @@ -2466,7 +2454,7 @@ CPUState *ppc405ep_init(MemoryRegion *address_space_mem, { clk_setup_t clk_setup[PPC405EP_CLK_NB], tlb_clk_setup; qemu_irq dma_irqs[4], gpt_irqs[5], mal_irqs[4]; - CPUState *env; + CPUPPCState *env; qemu_irq *pic, *irqs; memset(clk_setup, 0, sizeof(clk_setup)); @@ -2483,6 +2471,8 @@ CPUState *ppc405ep_init(MemoryRegion *address_space_mem, ppc4xx_pob_init(env); /* OBP arbitrer */ ppc4xx_opba_init(0xef600600); + /* Initialize timers */ + ppc_booke_timers_init(env, sysclk, 0); /* Universal interrupt controller */ irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); irqs[PPCUIC_OUTPUT_INT] = diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index f86b16838a..220c81d06e 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -121,7 +121,7 @@ out: } /* Create reset TLB entries for BookE, spanning the 32bit addr space. */ -static void mmubooke_create_initial_mapping(CPUState *env, +static void mmubooke_create_initial_mapping(CPUPPCState *env, target_ulong va, target_phys_addr_t pa) { @@ -145,9 +145,9 @@ static void mmubooke_create_initial_mapping(CPUState *env, static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUPPCState *env = opaque; - cpu_reset(env); + cpu_state_reset(env); env->gpr[1] = (16<<20) - 8; env->gpr[3] = FDT_ADDR; env->nip = entry; @@ -172,7 +172,7 @@ static void bamboo_init(ram_addr_t ram_size, qemu_irq *pic; qemu_irq *irqs; PCIBus *pcibus; - CPUState *env; + CPUPPCState *env; uint64_t elf_entry; uint64_t elf_lowaddr; target_phys_addr_t loadaddr = 0; diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h index f969e44e1b..b511020aeb 100644 --- a/hw/ppc4xx.h +++ b/hw/ppc4xx.h @@ -28,7 +28,7 @@ #include "pci.h" /* PowerPC 4xx core initialization */ -CPUState *ppc4xx_init (const char *cpu_model, +CPUPPCState *ppc4xx_init (const char *cpu_model, clk_setup_t *cpu_clk, clk_setup_t *tb_clk, uint32_t sysclk); @@ -38,7 +38,7 @@ enum { PPCUIC_OUTPUT_CINT = 1, PPCUIC_OUTPUT_NB, }; -qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs, +qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, uint32_t dcr_base, int has_ssr, int has_vr); ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, @@ -47,13 +47,13 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, target_phys_addr_t ram_sizes[], const unsigned int sdram_bank_sizes[]); -void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks, +void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, MemoryRegion ram_memories[], target_phys_addr_t *ram_bases, target_phys_addr_t *ram_sizes, int do_init); -PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4], +PCIBus *ppc4xx_pci_init(CPUPPCState *env, qemu_irq pci_irqs[4], target_phys_addr_t config_space, target_phys_addr_t int_ack, target_phys_addr_t special_cycle, diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c index 26040ac3ad..00e36f4109 100644 --- a/hw/ppc4xx_devs.c +++ b/hw/ppc4xx_devs.c @@ -38,13 +38,20 @@ # define LOG_UIC(...) do { } while (0) #endif +static void ppc4xx_reset(void *opaque) +{ + CPUPPCState *env = opaque; + + cpu_state_reset(env); +} + /*****************************************************************************/ /* Generic PowerPC 4xx processor instantiation */ -CPUState *ppc4xx_init (const char *cpu_model, +CPUPPCState *ppc4xx_init (const char *cpu_model, clk_setup_t *cpu_clk, clk_setup_t *tb_clk, uint32_t sysclk) { - CPUState *env; + CPUPPCState *env; /* init CPUs */ env = cpu_init(cpu_model); @@ -60,7 +67,7 @@ CPUState *ppc4xx_init (const char *cpu_model, tb_clk->opaque = env; ppc_dcr_init(env, NULL, NULL); /* Register qemu callbacks */ - qemu_register_reset((QEMUResetHandler*)&cpu_reset, env); + qemu_register_reset(ppc4xx_reset, env); return env; } @@ -288,7 +295,7 @@ static void ppcuic_reset (void *opaque) } } -qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs, +qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, uint32_t dcr_base, int has_ssr, int has_vr) { ppcuic_t *uic; @@ -634,7 +641,7 @@ static void sdram_reset (void *opaque) sdram->cfg = 0x00800000; } -void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks, +void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, MemoryRegion *ram_memories, target_phys_addr_t *ram_bases, target_phys_addr_t *ram_sizes, diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c index 88719458b0..d51e7fad67 100644 --- a/hw/ppc_booke.c +++ b/hw/ppc_booke.c @@ -71,7 +71,7 @@ struct booke_timer_t { uint32_t flags; }; -static void booke_update_irq(CPUState *env) +static void booke_update_irq(CPUPPCState *env) { ppc_set_irq(env, PPC_INTERRUPT_DECR, (env->spr[SPR_BOOKE_TSR] & TSR_DIS @@ -88,7 +88,7 @@ static void booke_update_irq(CPUState *env) /* Return the location of the bit of time base at which the FIT will raise an interrupt */ -static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env) +static uint8_t booke_get_fit_target(CPUPPCState *env, ppc_tb_t *tb_env) { uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT; @@ -106,7 +106,7 @@ static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env) /* Return the location of the bit of time base at which the WDT will raise an interrupt */ -static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env) +static uint8_t booke_get_wdt_target(CPUPPCState *env, ppc_tb_t *tb_env) { uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT; @@ -122,7 +122,7 @@ static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env) return wp; } -static void booke_update_fixed_timer(CPUState *env, +static void booke_update_fixed_timer(CPUPPCState *env, uint8_t target_bit, uint64_t *next, struct QEMUTimer *timer) @@ -153,7 +153,7 @@ static void booke_update_fixed_timer(CPUState *env, static void booke_decr_cb(void *opaque) { - CPUState *env = opaque; + CPUPPCState *env = opaque; env->spr[SPR_BOOKE_TSR] |= TSR_DIS; booke_update_irq(env); @@ -166,7 +166,7 @@ static void booke_decr_cb(void *opaque) static void booke_fit_cb(void *opaque) { - CPUState *env; + CPUPPCState *env; ppc_tb_t *tb_env; booke_timer_t *booke_timer; @@ -185,7 +185,7 @@ static void booke_fit_cb(void *opaque) static void booke_wdt_cb(void *opaque) { - CPUState *env; + CPUPPCState *env; ppc_tb_t *tb_env; booke_timer_t *booke_timer; @@ -203,13 +203,13 @@ static void booke_wdt_cb(void *opaque) booke_timer->wdt_timer); } -void store_booke_tsr(CPUState *env, target_ulong val) +void store_booke_tsr(CPUPPCState *env, target_ulong val) { env->spr[SPR_BOOKE_TSR] &= ~val; booke_update_irq(env); } -void store_booke_tcr(CPUState *env, target_ulong val) +void store_booke_tcr(CPUPPCState *env, target_ulong val) { ppc_tb_t *tb_env = env->tb_env; booke_timer_t *booke_timer = tb_env->opaque; @@ -231,7 +231,7 @@ void store_booke_tcr(CPUState *env, target_ulong val) } -void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags) +void ppc_booke_timers_init(CPUPPCState *env, uint32_t freq, uint32_t flags) { ppc_tb_t *tb_env; booke_timer_t *booke_timer; diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index 506187b182..879651018b 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -54,7 +54,6 @@ #include "nvram.h" #include "pc.h" #include "pci.h" -#include "usb-ohci.h" #include "net.h" #include "sysemu.h" #include "boards.h" @@ -122,6 +121,13 @@ static target_phys_addr_t round_page(target_phys_addr_t addr) return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; } +static void ppc_core99_reset(void *opaque) +{ + CPUPPCState *env = opaque; + + cpu_state_reset(env); +} + /* PowerPC Mac99 hardware initialisation */ static void ppc_core99_init (ram_addr_t ram_size, const char *boot_device, @@ -130,7 +136,7 @@ static void ppc_core99_init (ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - CPUState *env = NULL; + CPUPPCState *env = NULL; char *filename; qemu_irq *pic, **openpic_irqs; MemoryRegion *unin_memory = g_new(MemoryRegion, 1); @@ -167,7 +173,7 @@ static void ppc_core99_init (ram_addr_t ram_size, } /* Set time-base frequency to 100 Mhz */ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); - qemu_register_reset((QEMUResetHandler*)&cpu_reset, env); + qemu_register_reset(ppc_core99_reset, env); } /* allocate RAM */ @@ -352,7 +358,7 @@ static void ppc_core99_init (ram_addr_t ram_size, dbdma_mem, cuda_mem, NULL, 3, ide_mem, escc_bar); if (usb_enabled) { - usb_ohci_init_pci(pci_bus, -1); + pci_create_simple(pci_bus, -1, "pci-ohci"); } /* U3 needs to use USB for input because Linux doesn't support via-cuda diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index 9295a34f59..7e73d37c34 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -34,7 +34,6 @@ #include "net.h" #include "isa.h" #include "pci.h" -#include "usb-ohci.h" #include "boards.h" #include "fw_cfg.h" #include "escc.h" @@ -66,6 +65,13 @@ static target_phys_addr_t round_page(target_phys_addr_t addr) return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; } +static void ppc_heathrow_reset(void *opaque) +{ + CPUPPCState *env = opaque; + + cpu_state_reset(env); +} + static void ppc_heathrow_init (ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, @@ -74,7 +80,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, const char *cpu_model) { MemoryRegion *sysmem = get_system_memory(); - CPUState *env = NULL; + CPUPPCState *env = NULL; char *filename; qemu_irq *pic, **heathrow_irqs; int linux_boot, i; @@ -105,7 +111,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, } /* Set time-base frequency to 16.6 Mhz */ cpu_ppc_tb_init(env, 16600000UL); - qemu_register_reset((QEMUResetHandler*)&cpu_reset, env); + qemu_register_reset(ppc_heathrow_reset, env); } /* allocate RAM */ @@ -278,7 +284,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, dbdma_mem, cuda_mem, nvr, 2, ide_mem, escc_bar); if (usb_enabled) { - usb_ohci_init_pci(pci_bus, -1); + pci_create_simple(pci_bus, -1, "pci-ohci"); } if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index eb43fb5849..06d589d97b 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -30,7 +30,6 @@ #include "isa.h" #include "pci.h" #include "pci_host.h" -#include "usb-ohci.h" #include "ppc.h" #include "boards.h" #include "qemu-log.h" @@ -464,13 +463,20 @@ static const MemoryRegionOps PPC_prep_io_ops = { static void cpu_request_exit(void *opaque, int irq, int level) { - CPUState *env = cpu_single_env; + CPUPPCState *env = cpu_single_env; if (env && level) { cpu_exit(env); } } +static void ppc_prep_reset(void *opaque) +{ + CPUPPCState *env = opaque; + + cpu_state_reset(env); +} + /* PowerPC PREP hardware initialisation */ static void ppc_prep_init (ram_addr_t ram_size, const char *boot_device, @@ -480,7 +486,7 @@ static void ppc_prep_init (ram_addr_t ram_size, const char *cpu_model) { MemoryRegion *sysmem = get_system_memory(); - CPUState *env = NULL; + CPUPPCState *env = NULL; char *filename; nvram_t nvram; M48t59State *m48t59; @@ -525,7 +531,7 @@ static void ppc_prep_init (ram_addr_t ram_size, /* Set time-base frequency to 100 Mhz */ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); } - qemu_register_reset((QEMUResetHandler*)&cpu_reset, env); + qemu_register_reset(ppc_prep_reset, env); } /* allocate RAM */ @@ -688,7 +694,7 @@ static void ppc_prep_init (ram_addr_t ram_size, #endif if (usb_enabled) { - usb_ohci_init_pci(pci_bus, -1); + pci_create_simple(pci_bus, -1, "pci-ohci"); } m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 59); diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index d69f78cf33..5ee8cb34c1 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -58,7 +58,7 @@ struct boot_info uint32_t entry; }; -static int mpc8544_load_device_tree(CPUState *env, +static int mpc8544_load_device_tree(CPUPPCState *env, target_phys_addr_t addr, uint32_t ramsize, target_phys_addr_t initrd_base, @@ -178,7 +178,7 @@ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) return ffs(size >> 10) - 1; } -static void mmubooke_create_initial_mapping(CPUState *env, +static void mmubooke_create_initial_mapping(CPUPPCState *env, target_ulong va, target_phys_addr_t pa) { @@ -196,9 +196,9 @@ static void mmubooke_create_initial_mapping(CPUState *env, static void mpc8544ds_cpu_reset_sec(void *opaque) { - CPUState *env = opaque; + CPUPPCState *env = opaque; - cpu_reset(env); + cpu_state_reset(env); /* Secondary CPU starts in halted state for now. Needs to change when implementing non-kernel boot. */ @@ -208,10 +208,10 @@ static void mpc8544ds_cpu_reset_sec(void *opaque) static void mpc8544ds_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUPPCState *env = opaque; struct boot_info *bi = env->load_info; - cpu_reset(env); + cpu_state_reset(env); /* Set initial guest state. */ env->halted = 0; @@ -231,7 +231,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); PCIBus *pci_bus; - CPUState *env = NULL; + CPUPPCState *env = NULL; uint64_t elf_entry; uint64_t elf_lowaddr; target_phys_addr_t entry=0; @@ -244,7 +244,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; qemu_irq **irqs, *mpic; DeviceState *dev; - CPUState *firstenv = NULL; + CPUPPCState *firstenv = NULL; /* Setup CPUs */ if (cpu_model == NULL) { diff --git a/hw/ppce500_spin.c b/hw/ppce500_spin.c index 6b8a189c0e..960b7b0c3d 100644 --- a/hw/ppce500_spin.c +++ b/hw/ppce500_spin.c @@ -49,7 +49,7 @@ typedef struct spin_state { } SpinState; typedef struct spin_kick { - CPUState *env; + CPUPPCState *env; SpinInfo *spin; } SpinKick; @@ -73,7 +73,7 @@ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) return (ffs(size >> 10) - 1) >> 1; } -static void mmubooke_create_initial_mapping(CPUState *env, +static void mmubooke_create_initial_mapping(CPUPPCState *env, target_ulong va, target_phys_addr_t pa, target_phys_addr_t len) @@ -91,7 +91,7 @@ static void mmubooke_create_initial_mapping(CPUState *env, static void spin_kick(void *data) { SpinKick *kick = data; - CPUState *env = kick->env; + CPUPPCState *env = kick->env; SpinInfo *curspin = kick->spin; target_phys_addr_t map_size = 64 * 1024 * 1024; target_phys_addr_t map_start; @@ -121,7 +121,7 @@ static void spin_write(void *opaque, target_phys_addr_t addr, uint64_t value, { SpinState *s = opaque; int env_idx = addr / sizeof(SpinInfo); - CPUState *env; + CPUPPCState *env; SpinInfo *curspin = &s->spin[env_idx]; uint8_t *curspin_p = (uint8_t*)curspin; @@ -182,7 +182,7 @@ static uint64_t spin_read(void *opaque, target_phys_addr_t addr, unsigned len) } } -const MemoryRegionOps spin_rw_ops = { +static const MemoryRegionOps spin_rw_ops = { .read = spin_read, .write = spin_write, .endianness = DEVICE_BIG_ENDIAN, diff --git a/hw/pxa.h b/hw/pxa.h index e7787393c7..025be34f86 100644 --- a/hw/pxa.h +++ b/hw/pxa.h @@ -65,11 +65,11 @@ # define PXA2XX_INTERNAL_SIZE 0x40000 /* pxa2xx_pic.c */ -DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env); +DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUARMState *env); /* pxa2xx_gpio.c */ DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, - CPUState *env, DeviceState *pic, int lines); + CPUARMState *env, DeviceState *pic, int lines); void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler); /* pxa2xx_dma.c */ @@ -122,7 +122,7 @@ typedef struct PXA2xxI2SState PXA2xxI2SState; typedef struct PXA2xxFIrState PXA2xxFIrState; typedef struct { - CPUState *env; + CPUARMState *env; DeviceState *pic; qemu_irq reset; MemoryRegion sdram; diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index 1ab27012c1..1d5c35f174 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -1507,8 +1507,7 @@ PXA2xxI2CState *pxa2xx_i2c_init(target_phys_addr_t base, i2c_dev = sysbus_from_qdev(qdev_create(NULL, "pxa2xx_i2c")); qdev_prop_set_uint32(&i2c_dev->qdev, "size", region_size + 1); - qdev_prop_set_uint32(&i2c_dev->qdev, "offset", - base - (base & (~region_size) & TARGET_PAGE_MASK)); + qdev_prop_set_uint32(&i2c_dev->qdev, "offset", base & region_size); qdev_init_nofail(&i2c_dev->qdev); @@ -2045,7 +2044,7 @@ static void pxa2xx_reset(void *opaque, int line, int level) PXA2xxState *s = (PXA2xxState *) opaque; if (level && (s->pm_regs[PCFR >> 2] & 0x10)) { /* GPR_EN */ - cpu_reset(s->env); + cpu_state_reset(s->env); /* TODO: reset peripherals */ } } diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c index 8ced0dd8ec..031015400b 100644 --- a/hw/pxa2xx_dma.c +++ b/hw/pxa2xx_dma.c @@ -18,9 +18,9 @@ #define PXA2XX_DMA_NUM_REQUESTS 75 typedef struct { - target_phys_addr_t descr; - target_phys_addr_t src; - target_phys_addr_t dest; + uint32_t descr; + uint32_t src; + uint32_t dest; uint32_t cmd; uint32_t state; int request; @@ -512,9 +512,9 @@ static VMStateDescription vmstate_pxa2xx_dma_chan = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { - VMSTATE_UINTTL(descr, PXA2xxDMAChannel), - VMSTATE_UINTTL(src, PXA2xxDMAChannel), - VMSTATE_UINTTL(dest, PXA2xxDMAChannel), + VMSTATE_UINT32(descr, PXA2xxDMAChannel), + VMSTATE_UINT32(src, PXA2xxDMAChannel), + VMSTATE_UINT32(dest, PXA2xxDMAChannel), VMSTATE_UINT32(cmd, PXA2xxDMAChannel), VMSTATE_UINT32(state, PXA2xxDMAChannel), VMSTATE_INT32(request, PXA2xxDMAChannel), diff --git a/hw/pxa2xx_gpio.c b/hw/pxa2xx_gpio.c index d5f57162ed..09a408b781 100644 --- a/hw/pxa2xx_gpio.c +++ b/hw/pxa2xx_gpio.c @@ -20,7 +20,7 @@ struct PXA2xxGPIOInfo { qemu_irq irq0, irq1, irqX; int lines; int ncpu; - CPUState *cpu_env; + CPUARMState *cpu_env; /* XXX: GNU C vectors are more suitable */ uint32_t ilevel[PXA2XX_GPIO_BANKS]; @@ -249,7 +249,7 @@ static const MemoryRegionOps pxa_gpio_ops = { }; DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, - CPUState *env, DeviceState *pic, int lines) + CPUARMState *env, DeviceState *pic, int lines) { DeviceState *dev; diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c index fcbdfb3fba..ee8bf577cb 100644 --- a/hw/pxa2xx_lcd.c +++ b/hw/pxa2xx_lcd.c @@ -19,15 +19,15 @@ #include "framebuffer.h" struct DMAChannel { - target_phys_addr_t branch; + uint32_t branch; uint8_t up; uint8_t palette[1024]; uint8_t pbuffer[1024]; void (*redraw)(PXA2xxLCDState *s, target_phys_addr_t addr, int *miny, int *maxy); - target_phys_addr_t descriptor; - target_phys_addr_t source; + uint32_t descriptor; + uint32_t source; uint32_t id; uint32_t command; }; @@ -929,11 +929,11 @@ static const VMStateDescription vmstate_dma_channel = { .minimum_version_id = 0, .minimum_version_id_old = 0, .fields = (VMStateField[]) { - VMSTATE_UINTTL(branch, struct DMAChannel), + VMSTATE_UINT32(branch, struct DMAChannel), VMSTATE_UINT8(up, struct DMAChannel), VMSTATE_BUFFER(pbuffer, struct DMAChannel), - VMSTATE_UINTTL(descriptor, struct DMAChannel), - VMSTATE_UINTTL(source, struct DMAChannel), + VMSTATE_UINT32(descriptor, struct DMAChannel), + VMSTATE_UINT32(source, struct DMAChannel), VMSTATE_UINT32(id, struct DMAChannel), VMSTATE_UINT32(command, struct DMAChannel), VMSTATE_END_OF_LIST() diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c index 6b2bdb0df1..a806b80b0f 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/pxa2xx_pic.c @@ -34,7 +34,7 @@ typedef struct { SysBusDevice busdev; MemoryRegion iomem; - CPUState *cpu_env; + CPUARMState *cpu_env; uint32_t int_enabled[2]; uint32_t int_pending[2]; uint32_t is_fiq[2]; @@ -245,7 +245,7 @@ static int pxa2xx_pic_post_load(void *opaque, int version_id) return 0; } -DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env) +DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUARMState *env) { DeviceState *dev = qdev_create(NULL, "pxa2xx_pic"); PXA2xxPICState *s = FROM_SYSBUS(PXA2xxPICState, sysbus_from_qdev(dev)); diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index 0423af1c31..bff9152df5 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -421,10 +421,6 @@ static void set_string(Object *obj, Visitor *v, void *opaque, error_propagate(errp, local_err); return; } - if (!*str) { - g_free(str); - str = NULL; - } if (*ptr) { g_free(*ptr); } diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 708414376e..28ab182226 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -21,14 +21,30 @@ #include "qxl.h" -static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) +static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { - uint8_t *src = qxl->guest_primary.data; - uint8_t *dst = qxl->guest_primary.flipped; + uint8_t *src; + uint8_t *dst = qxl->vga.ds->surface->data; int len, i; - src += (qxl->guest_primary.surface.height - rect->top - 1) * - qxl->guest_primary.abs_stride; + if (is_buffer_shared(qxl->vga.ds->surface)) { + return; + } + if (!qxl->guest_primary.data) { + trace_qxl_render_blit_guest_primary_initialized(); + qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); + } + trace_qxl_render_blit(qxl->guest_primary.qxl_stride, + rect->left, rect->right, rect->top, rect->bottom); + src = qxl->guest_primary.data; + if (qxl->guest_primary.qxl_stride < 0) { + /* qxl surface is upside down, walk src scanlines + * in reverse order to flip it */ + src += (qxl->guest_primary.surface.height - rect->top - 1) * + qxl->guest_primary.abs_stride; + } else { + src += rect->top * qxl->guest_primary.abs_stride; + } dst += rect->top * qxl->guest_primary.abs_stride; src += rect->left * qxl->guest_primary.bytes_pp; dst += rect->left * qxl->guest_primary.bytes_pp; @@ -37,7 +53,7 @@ static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) for (i = rect->top; i < rect->bottom; i++) { memcpy(dst, src, len); dst += qxl->guest_primary.abs_stride; - src -= qxl->guest_primary.abs_stride; + src += qxl->guest_primary.qxl_stride; } } @@ -71,84 +87,105 @@ void qxl_render_resize(PCIQXLDevice *qxl) } } -void qxl_render_update(PCIQXLDevice *qxl) +static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) +{ + area->left = 0; + area->right = qxl->guest_primary.surface.width; + area->top = 0; + area->bottom = qxl->guest_primary.surface.height; +} + +static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; - QXLRect dirty[32], update; - void *ptr; - int i, redraw = 0; - - if (!is_buffer_shared(vga->ds->surface)) { - dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__); - qxl->guest_primary.resized++; - qxl->guest_primary.commands++; - redraw = 1; - } + int i; + DisplaySurface *surface = vga->ds->surface; if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; - - if (qxl->guest_primary.flipped) { - g_free(qxl->guest_primary.flipped); - qxl->guest_primary.flipped = NULL; - } - qemu_free_displaysurface(vga->ds); - qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); - if (qxl->guest_primary.qxl_stride < 0) { - /* spice surface is upside down -> need extra buffer to flip */ - qxl->guest_primary.flipped = - g_malloc(qxl->guest_primary.surface.width * - qxl->guest_primary.abs_stride); - ptr = qxl->guest_primary.flipped; - } else { - ptr = qxl->guest_primary.data; - } - dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n", - __FUNCTION__, + qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); + qxl->num_dirty_rects = 1; + trace_qxl_render_guest_primary_resized( qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, - qxl->guest_primary.bits_pp, - qxl->guest_primary.flipped ? "yes" : "no"); - vga->ds->surface = + qxl->guest_primary.bits_pp); + } + if (surface->width != qxl->guest_primary.surface.width || + surface->height != qxl->guest_primary.surface.height) { + if (qxl->guest_primary.qxl_stride > 0) { + qemu_free_displaysurface(vga->ds); qemu_create_displaysurface_from(qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.abs_stride, - ptr); - dpy_resize(vga->ds); - } - - update.left = 0; - update.right = qxl->guest_primary.surface.width; - update.top = 0; - update.bottom = qxl->guest_primary.surface.height; - - memset(dirty, 0, sizeof(dirty)); - if (runstate_is_running() && qxl->guest_primary.commands) { - qxl->guest_primary.commands = 0; - qxl_spice_update_area(qxl, 0, &update, - dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC); - } - if (redraw) { - memset(dirty, 0, sizeof(dirty)); - dirty[0] = update; + qxl->guest_primary.data); + } else { + qemu_resize_displaysurface(vga->ds, + qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height); + } } - - for (i = 0; i < ARRAY_SIZE(dirty); i++) { - if (qemu_spice_rect_is_empty(dirty+i)) { + for (i = 0; i < qxl->num_dirty_rects; i++) { + if (qemu_spice_rect_is_empty(qxl->dirty+i)) { break; } - if (qxl->guest_primary.flipped) { - qxl_flip(qxl, dirty+i); - } + qxl_blit(qxl, qxl->dirty+i); dpy_update(vga->ds, - dirty[i].left, dirty[i].top, - dirty[i].right - dirty[i].left, - dirty[i].bottom - dirty[i].top); + qxl->dirty[i].left, qxl->dirty[i].top, + qxl->dirty[i].right - qxl->dirty[i].left, + qxl->dirty[i].bottom - qxl->dirty[i].top); } + qxl->num_dirty_rects = 0; +} + +/* + * use ssd.lock to protect render_update_cookie_num. + * qxl_render_update is called by io thread or vcpu thread, and the completion + * callbacks are called by spice_server thread, defering to bh called from the + * io thread. + */ +void qxl_render_update(PCIQXLDevice *qxl) +{ + QXLCookie *cookie; + + qemu_mutex_lock(&qxl->ssd.lock); + + if (!runstate_is_running() || !qxl->guest_primary.commands) { + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + + qxl->guest_primary.commands = 0; + qxl->render_update_cookie_num++; + qemu_mutex_unlock(&qxl->ssd.lock); + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, + 0); + qxl_set_rect_to_surface(qxl, &cookie->u.render.area); + qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL, + 0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie); +} + +void qxl_render_update_area_bh(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + + qemu_mutex_lock(&qxl->ssd.lock); + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) +{ + qemu_mutex_lock(&qxl->ssd.lock); + trace_qxl_render_update_area_done(cookie); + qemu_bh_schedule(qxl->update_area_bh); + qxl->render_update_cookie_num--; + qemu_mutex_unlock(&qxl->ssd.lock); + g_free(cookie); } static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor) diff --git a/hw/qxl.c b/hw/qxl.c index f643667205..47a162e479 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -23,6 +23,7 @@ #include "qemu-queue.h" #include "monitor.h" #include "sysemu.h" +#include "trace.h" #include "qxl.h" @@ -125,9 +126,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl); void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) { -#if SPICE_INTERFACE_QXL_MINOR >= 1 qxl_send_events(qxl, QXL_INTERRUPT_ERROR); -#endif if (qxl->guestdebug) { va_list ap; va_start(ap, msg); @@ -143,24 +142,26 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, uint32_t clear_dirty_region, - qxl_async_io async) + qxl_async_io async, struct QXLCookie *cookie) { + trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right, + area->top, area->bottom); + trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects, + clear_dirty_region); if (async == QXL_SYNC) { qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, dirty_rects, num_dirty_rects, clear_dirty_region); } else { -#if SPICE_INTERFACE_QXL_MINOR >= 1 + assert(cookie != NULL); spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, - clear_dirty_region, 0); -#else - abort(); -#endif + clear_dirty_region, (uintptr_t)cookie); } } static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, uint32_t id) { + trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id); qemu_mutex_lock(&qxl->track_lock); qxl->guest_surfaces.cmds[id] = 0; qxl->guest_surfaces.count--; @@ -170,44 +171,50 @@ static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, qxl_async_io async) { + QXLCookie *cookie; + + trace_qxl_spice_destroy_surface_wait(qxl->id, id, async); if (async) { -#if SPICE_INTERFACE_QXL_MINOR < 1 - abort(); -#else - spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, - (uint64_t)id); -#endif + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_SURFACE_ASYNC); + cookie->u.surface_id = id; + spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie); } else { qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); - qxl_spice_destroy_surface_wait_complete(qxl, id); } } -#if SPICE_INTERFACE_QXL_MINOR >= 1 static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) { - spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0); + trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count, + qxl->num_free_res); + spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_FLUSH_SURFACES_ASYNC)); } -#endif void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count) { + trace_qxl_spice_loadvm_commands(qxl->id, ext, count); qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count); } void qxl_spice_oom(PCIQXLDevice *qxl) { + trace_qxl_spice_oom(qxl->id); qxl->ssd.worker->oom(qxl->ssd.worker); } void qxl_spice_reset_memslots(PCIQXLDevice *qxl) { + trace_qxl_spice_reset_memslots(qxl->id); qxl->ssd.worker->reset_memslots(qxl->ssd.worker); } static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) { + trace_qxl_spice_destroy_surfaces_complete(qxl->id); qemu_mutex_lock(&qxl->track_lock); memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds)); qxl->guest_surfaces.count = 0; @@ -216,12 +223,11 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) { + trace_qxl_spice_destroy_surfaces(qxl->id, async); if (async) { -#if SPICE_INTERFACE_QXL_MINOR < 1 - abort(); -#else - spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0); -#endif + spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_ALL_SURFACES_ASYNC)); } else { qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); qxl_spice_destroy_surfaces_complete(qxl); @@ -230,11 +236,13 @@ static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) { + trace_qxl_spice_reset_image_cache(qxl->id); qxl->ssd.worker->reset_image_cache(qxl->ssd.worker); } void qxl_spice_reset_cursor(PCIQXLDevice *qxl) { + trace_qxl_spice_reset_cursor(qxl->id); qxl->ssd.worker->reset_cursor(qxl->ssd.worker); qemu_mutex_lock(&qxl->track_lock); qxl->guest_cursor = 0; @@ -416,7 +424,7 @@ static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - dprint(qxl, 1, "%s:\n", __FUNCTION__); + trace_qxl_interface_attach_worker(qxl->id); qxl->ssd.worker = qxl_worker; } @@ -424,7 +432,7 @@ static void interface_set_compression_level(QXLInstance *sin, int level) { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - dprint(qxl, 1, "%s: %d\n", __FUNCTION__, level); + trace_qxl_interface_set_compression_level(qxl->id, level); qxl->shadow_rom.compression_level = cpu_to_le32(level); qxl->rom->compression_level = cpu_to_le32(level); qxl_rom_set_dirty(qxl); @@ -434,6 +442,7 @@ static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + trace_qxl_interface_set_mm_time(qxl->id, mm_time); qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time); qxl->rom->mm_clock = cpu_to_le32(mm_time); qxl_rom_set_dirty(qxl); @@ -443,7 +452,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - dprint(qxl, 1, "%s:\n", __FUNCTION__); + trace_qxl_interface_get_init_info(qxl->id); info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; info->memslot_id_bits = MEMSLOT_SLOT_BITS; info->num_memslots = NUM_MEMSLOTS; @@ -490,7 +499,6 @@ static const char *io_port_to_string(uint32_t io_port) [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", -#if SPICE_INTERFACE_QXL_MINOR >= 1 [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", @@ -500,7 +508,6 @@ static const char *io_port_to_string(uint32_t io_port) = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", -#endif }; return io_port_to_string[io_port]; } @@ -514,9 +521,10 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) QXLCommand *cmd; int notify, ret; + trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode)); + switch (qxl->mode) { case QXL_MODE_VGA: - dprint(qxl, 2, "%s: vga\n", __FUNCTION__); ret = false; qemu_mutex_lock(&qxl->ssd.lock); if (qxl->ssd.update != NULL) { @@ -527,19 +535,18 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) } qemu_mutex_unlock(&qxl->ssd.lock); if (ret) { - dprint(qxl, 2, "%s %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); + trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); qxl_log_command(qxl, "vga", ext); } return ret; case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: case QXL_MODE_UNDEFINED: - dprint(qxl, 4, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); ring = &qxl->ram->cmd_ring; if (SPICE_RING_IS_EMPTY(ring)) { return false; } - dprint(qxl, 2, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); + trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); SPICE_RING_CONS_ITEM(ring, cmd); ext->cmd = *cmd; ext->group_id = MEMSLOT_GROUP_GUEST; @@ -564,6 +571,7 @@ static int interface_req_cmd_notification(QXLInstance *sin) PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); int wait = 1; + trace_qxl_ring_command_req_notification(qxl->id); switch (qxl->mode) { case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: @@ -601,10 +609,11 @@ static inline void qxl_push_free_res(PCIQXLDevice *d, int flush) } SPICE_RING_PUSH(ring, notify); - dprint(d, 2, "free: push %d items, notify %s, ring %d/%d [%d,%d]\n", - d->num_free_res, notify ? "yes" : "no", - ring->prod - ring->cons, ring->num_items, - ring->prod, ring->cons); + trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->mode), + d->guest_surfaces.count, d->num_free_res, + d->last_release, notify ? "yes" : "no"); + trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons, + ring->num_items, ring->prod, ring->cons); if (notify) { qxl_send_events(d, QXL_INTERRUPT_DISPLAY); } @@ -651,7 +660,7 @@ static void interface_release_resource(QXLInstance *sin, } qxl->last_release = ext.info; qxl->num_free_res++; - dprint(qxl, 3, "%4d\r", qxl->num_free_res); + trace_qxl_ring_res_put(qxl->id, qxl->num_free_res); qxl_push_free_res(qxl, 0); } @@ -663,6 +672,8 @@ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt * QXLCommand *cmd; int notify; + trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode)); + switch (qxl->mode) { case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: @@ -686,6 +697,7 @@ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt * if (qxl->id == 0) { qxl_render_cursor(qxl, ext); } + trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode)); return true; default: return false; @@ -698,6 +710,7 @@ static int interface_req_cursor_notification(QXLInstance *sin) PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); int wait = 1; + trace_qxl_ring_cursor_req_notification(qxl->id); switch (qxl->mode) { case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: @@ -725,7 +738,6 @@ static int interface_flush_resources(QXLInstance *sin) PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); int ret; - dprint(qxl, 1, "free: guest flush (have %d)\n", qxl->num_free_res); ret = qxl->num_free_res; if (ret) { qxl_push_free_res(qxl, 1); @@ -735,12 +747,9 @@ static int interface_flush_resources(QXLInstance *sin) static void qxl_create_guest_primary_complete(PCIQXLDevice *d); -#if SPICE_INTERFACE_QXL_MINOR >= 1 - /* called from spice server thread context only */ -static void interface_async_complete(QXLInstance *sin, uint64_t cookie) +static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) { - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); uint32_t current_async; qemu_mutex_lock(&qxl->async_lock); @@ -748,9 +757,22 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie) qxl->current_async = QXL_UNDEFINED_IO; qemu_mutex_unlock(&qxl->async_lock); - dprint(qxl, 2, "async_complete: %d (%" PRId64 ") done\n", - current_async, cookie); + trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie); + if (!cookie) { + fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__); + return; + } + if (cookie && current_async != cookie->io) { + fprintf(stderr, + "qxl: %s: error: current_async = %d != %" PRId64 " = cookie->io\n", + __func__, current_async, cookie->io); + } switch (current_async) { + case QXL_IO_MEMSLOT_ADD_ASYNC: + case QXL_IO_DESTROY_PRIMARY_ASYNC: + case QXL_IO_UPDATE_AREA_ASYNC: + case QXL_IO_FLUSH_SURFACES_ASYNC: + break; case QXL_IO_CREATE_PRIMARY_ASYNC: qxl_create_guest_primary_complete(qxl); break; @@ -758,13 +780,79 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie) qxl_spice_destroy_surfaces_complete(qxl); break; case QXL_IO_DESTROY_SURFACE_ASYNC: - qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie); + qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id); break; + default: + fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, + current_async); } qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); } -#endif +/* called from spice server thread context only */ +static void interface_update_area_complete(QXLInstance *sin, + uint32_t surface_id, + QXLRect *dirty, uint32_t num_updated_rects) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int i; + int qxl_i; + + qemu_mutex_lock(&qxl->ssd.lock); + if (surface_id != 0 || !qxl->render_update_cookie_num) { + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + trace_qxl_interface_update_area_complete(qxl->id, surface_id, dirty->left, + dirty->right, dirty->top, dirty->bottom); + trace_qxl_interface_update_area_complete_rest(qxl->id, num_updated_rects); + if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) { + /* + * overflow - treat this as a full update. Not expected to be common. + */ + trace_qxl_interface_update_area_complete_overflow(qxl->id, + QXL_NUM_DIRTY_RECTS); + qxl->guest_primary.resized = 1; + } + if (qxl->guest_primary.resized) { + /* + * Don't bother copying or scheduling the bh since we will flip + * the whole area anyway on completion of the update_area async call + */ + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + qxl_i = qxl->num_dirty_rects; + for (i = 0; i < num_updated_rects; i++) { + qxl->dirty[qxl_i++] = dirty[i]; + } + qxl->num_dirty_rects += num_updated_rects; + trace_qxl_interface_update_area_complete_schedule_bh(qxl->id, + qxl->num_dirty_rects); + qemu_bh_schedule(qxl->update_area_bh); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +/* called from spice server thread context only */ +static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; + + switch (cookie->type) { + case QXL_COOKIE_TYPE_IO: + interface_async_complete_io(qxl, cookie); + g_free(cookie); + break; + case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA: + qxl_render_update_area_done(qxl, cookie); + break; + default: + fprintf(stderr, "qxl: %s: unexpected cookie type %d\n", + __func__, cookie->type); + g_free(cookie); + } +} static const QXLInterface qxl_interface = { .base.type = SPICE_INTERFACE_QXL, @@ -785,9 +873,8 @@ static const QXLInterface qxl_interface = { .req_cursor_notification = interface_req_cursor_notification, .notify_update = interface_notify_update, .flush_resources = interface_flush_resources, -#if SPICE_INTERFACE_QXL_MINOR >= 1 .async_complete = interface_async_complete, -#endif + .update_area_complete = interface_update_area_complete, }; static void qxl_enter_vga_mode(PCIQXLDevice *d) @@ -795,7 +882,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d) if (d->mode == QXL_MODE_VGA) { return; } - dprint(d, 1, "%s\n", __FUNCTION__); + trace_qxl_enter_vga_mode(d->id); qemu_spice_create_host_primary(&d->ssd); d->mode = QXL_MODE_VGA; memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty)); @@ -806,7 +893,7 @@ static void qxl_exit_vga_mode(PCIQXLDevice *d) if (d->mode != QXL_MODE_VGA) { return; } - dprint(d, 1, "%s\n", __FUNCTION__); + trace_qxl_exit_vga_mode(d->id); qxl_destroy_primary(d, QXL_SYNC); } @@ -843,7 +930,7 @@ static void qxl_reset_state(PCIQXLDevice *d) static void qxl_soft_reset(PCIQXLDevice *d) { - dprint(d, 1, "%s:\n", __FUNCTION__); + trace_qxl_soft_reset(d->id); qxl_check_state(d); if (d->id == 0) { @@ -855,8 +942,7 @@ static void qxl_soft_reset(PCIQXLDevice *d) static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) { - dprint(d, 1, "%s: start%s\n", __FUNCTION__, - loadvm ? " (loadvm)" : ""); + trace_qxl_hard_reset(d->id, loadvm); qxl_spice_reset_cursor(d); qxl_spice_reset_image_cache(d); @@ -871,13 +957,12 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) } qemu_spice_create_host_memslot(&d->ssd); qxl_soft_reset(d); - - dprint(d, 1, "%s: done\n", __FUNCTION__); } static void qxl_reset_handler(DeviceState *dev) { PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev); + qxl_hard_reset(d, 0); } @@ -886,8 +971,8 @@ static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) VGACommonState *vga = opaque; PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga); + trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val); if (qxl->mode != QXL_MODE_VGA) { - dprint(qxl, 1, "%s\n", __FUNCTION__); qxl_destroy_primary(qxl, QXL_SYNC); qxl_soft_reset(qxl); } @@ -914,6 +999,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, static const int regions[] = { QXL_RAM_RANGE_INDEX, QXL_VRAM_RANGE_INDEX, + QXL_VRAM64_RANGE_INDEX, }; uint64_t guest_start; uint64_t guest_end; @@ -927,9 +1013,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start); guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end); - dprint(d, 1, "%s: slot %d: guest phys 0x%" PRIx64 " - 0x%" PRIx64 "\n", - __FUNCTION__, slot_id, - guest_start, guest_end); + trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end); PANIC_ON(slot_id >= NUM_MEMSLOTS); PANIC_ON(guest_start > guest_end); @@ -960,6 +1044,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); break; case QXL_VRAM_RANGE_INDEX: + case 4 /* vram 64bit */: virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); break; default: @@ -975,10 +1060,6 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, memslot.generation = d->rom->slot_generation = 0; qxl_rom_set_dirty(d); - dprint(d, 1, "%s: slot %d: host virt 0x%lx - 0x%lx\n", - __FUNCTION__, memslot.slot_id, - memslot.virt_start, memslot.virt_end); - qemu_spice_add_memslot(&d->ssd, &memslot, async); d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; @@ -988,21 +1069,19 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) { - dprint(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id); qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id); d->guest_slots[slot_id].active = 0; } static void qxl_reset_memslots(PCIQXLDevice *d) { - dprint(d, 1, "%s:\n", __FUNCTION__); qxl_spice_reset_memslots(d); memset(&d->guest_slots, 0, sizeof(d->guest_slots)); } static void qxl_reset_surfaces(PCIQXLDevice *d) { - dprint(d, 1, "%s:\n", __FUNCTION__); + trace_qxl_reset_surfaces(d->id); d->mode = QXL_MODE_UNDEFINED; qxl_spice_destroy_surfaces(d, QXL_SYNC); } @@ -1044,9 +1123,6 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, assert(qxl->mode != QXL_MODE_NATIVE); qxl_exit_vga_mode(qxl); - dprint(qxl, 1, "%s: %dx%d\n", __FUNCTION__, - le32_to_cpu(sc->width), le32_to_cpu(sc->height)); - surface.format = le32_to_cpu(sc->format); surface.height = le32_to_cpu(sc->height); surface.mem = le64_to_cpu(sc->mem); @@ -1055,6 +1131,10 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, surface.width = le32_to_cpu(sc->width); surface.type = le32_to_cpu(sc->type); surface.flags = le32_to_cpu(sc->flags); + trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem, + sc->format, sc->position); + trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type, + sc->flags); surface.mouse_mode = true; surface.group_id = MEMSLOT_GROUP_GUEST; @@ -1078,9 +1158,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) if (d->mode == QXL_MODE_UNDEFINED) { return 0; } - - dprint(d, 1, "%s\n", __FUNCTION__); - + trace_qxl_destroy_primary(d->id); d->mode = QXL_MODE_UNDEFINED; qemu_spice_destroy_primary_surface(&d->ssd, 0, async); qxl_spice_reset_cursor(d); @@ -1107,8 +1185,8 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) .mem = devmem + d->shadow_rom.draw_area_offset, }; - dprint(d, 1, "%s: mode %d [ %d x %d @ %d bpp devmem 0x%" PRIx64 " ]\n", - __func__, modenr, mode->x_res, mode->y_res, mode->bits, devmem); + trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits, + devmem); if (!loadvm) { qxl_hard_reset(d, 0); } @@ -1137,9 +1215,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, PCIQXLDevice *d = opaque; uint32_t io_port = addr; qxl_async_io async = QXL_SYNC; -#if SPICE_INTERFACE_QXL_MINOR >= 1 uint32_t orig_io_port = io_port; -#endif switch (io_port) { case QXL_IO_RESET: @@ -1149,28 +1225,23 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, case QXL_IO_CREATE_PRIMARY: case QXL_IO_UPDATE_IRQ: case QXL_IO_LOG: -#if SPICE_INTERFACE_QXL_MINOR >= 1 case QXL_IO_MEMSLOT_ADD_ASYNC: case QXL_IO_CREATE_PRIMARY_ASYNC: -#endif break; default: if (d->mode != QXL_MODE_VGA) { break; } - dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n", - __func__, io_port, io_port_to_string(io_port)); -#if SPICE_INTERFACE_QXL_MINOR >= 1 + trace_qxl_io_unexpected_vga_mode(d->id, + io_port, io_port_to_string(io_port)); /* be nice to buggy guest drivers */ if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); } -#endif return; } -#if SPICE_INTERFACE_QXL_MINOR >= 1 /* we change the io_port to avoid ifdeffery in the main switch */ orig_io_port = io_port; switch (io_port) { @@ -1204,19 +1275,27 @@ async_common: } d->current_async = orig_io_port; qemu_mutex_unlock(&d->async_lock); - dprint(d, 2, "start async %d (%"PRId64")\n", io_port, val); break; default: break; } -#endif + trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), addr, val, size, + async); switch (io_port) { case QXL_IO_UPDATE_AREA: { + QXLCookie *cookie = NULL; QXLRect update = d->ram->update_area; + + if (async == QXL_ASYNC) { + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_UPDATE_AREA_ASYNC); + cookie->u.area = update; + } qxl_spice_update_area(d, d->ram->update_surface, - &update, NULL, 0, 0, async); + cookie ? &cookie->u.area : &update, + NULL, 0, 0, async, cookie); break; } case QXL_IO_NOTIFY_CMD: @@ -1237,7 +1316,6 @@ async_common: d->oom_running = 0; break; case QXL_IO_SET_MODE: - dprint(d, 1, "QXL_SET_MODE %d\n", (int)val); qxl_set_mode(d, val, 0); break; case QXL_IO_LOG: @@ -1247,7 +1325,6 @@ async_common: } break; case QXL_IO_RESET: - dprint(d, 1, "QXL_IO_RESET\n"); qxl_hard_reset(d, 0); break; case QXL_IO_MEMSLOT_ADD: @@ -1275,7 +1352,6 @@ async_common: async); goto cancel_async; } - dprint(d, 1, "QXL_IO_CREATE_PRIMARY async=%d\n", async); d->guest_primary.surface = d->ram->create_surface; qxl_create_guest_primary(d, 0, async); break; @@ -1285,11 +1361,9 @@ async_common: async); goto cancel_async; } - dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (async=%d) (%s)\n", async, - qxl_mode_to_string(d->mode)); if (!qxl_destroy_primary(d, async)) { - dprint(d, 1, "QXL_IO_DESTROY_PRIMARY_ASYNC in %s, ignored\n", - qxl_mode_to_string(d->mode)); + trace_qxl_io_destroy_primary_ignored(d->id, + qxl_mode_to_string(d->mode)); goto cancel_async; } break; @@ -1301,7 +1375,6 @@ async_common: } qxl_spice_destroy_surface_wait(d, val, async); break; -#if SPICE_INTERFACE_QXL_MINOR >= 1 case QXL_IO_FLUSH_RELEASE: { QXLReleaseRing *ring = &d->ram->release_ring; if (ring->prod - ring->cons + 1 == ring->num_items) { @@ -1310,19 +1383,11 @@ async_common: ring->prod, ring->cons); } qxl_push_free_res(d, 1 /* flush */); - dprint(d, 1, "QXL_IO_FLUSH_RELEASE exit (%s, s#=%d, res#=%d,%p)\n", - qxl_mode_to_string(d->mode), d->guest_surfaces.count, - d->num_free_res, d->last_release); break; } case QXL_IO_FLUSH_SURFACES_ASYNC: - dprint(d, 1, "QXL_IO_FLUSH_SURFACES_ASYNC" - " (%"PRId64") (%s, s#=%d, res#=%d)\n", - val, qxl_mode_to_string(d->mode), d->guest_surfaces.count, - d->num_free_res); qxl_spice_flush_surfaces_async(d); break; -#endif case QXL_IO_DESTROY_ALL_SURFACES: d->mode = QXL_MODE_UNDEFINED; qxl_spice_destroy_surfaces(d, async); @@ -1333,16 +1398,12 @@ async_common: } return; cancel_async: -#if SPICE_INTERFACE_QXL_MINOR >= 1 if (async) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); qemu_mutex_lock(&d->async_lock); d->current_async = QXL_UNDEFINED_IO; qemu_mutex_unlock(&d->async_lock); } -#else - return; -#endif } static uint64_t ioport_read(void *opaque, target_phys_addr_t addr, @@ -1350,7 +1411,7 @@ static uint64_t ioport_read(void *opaque, target_phys_addr_t addr, { PCIQXLDevice *d = opaque; - dprint(d, 1, "%s: unexpected\n", __FUNCTION__); + trace_qxl_io_read_unexpected(d->id); return 0xff; } @@ -1396,16 +1457,17 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events) static void init_pipe_signaling(PCIQXLDevice *d) { - if (pipe(d->pipe) < 0) { - dprint(d, 1, "%s: pipe creation failed\n", __FUNCTION__); - return; - } - fcntl(d->pipe[0], F_SETFL, O_NONBLOCK); - fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); - fcntl(d->pipe[0], F_SETOWN, getpid()); + if (pipe(d->pipe) < 0) { + fprintf(stderr, "%s:%s: qxl pipe creation failed\n", + __FILE__, __func__); + exit(1); + } + fcntl(d->pipe[0], F_SETFL, O_NONBLOCK); + fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); + fcntl(d->pipe[0], F_SETOWN, getpid()); - qemu_thread_get_self(&d->main); - qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); + qemu_thread_get_self(&d->main); + qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); } /* graphics console */ @@ -1500,8 +1562,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) surface_offset -= vram_start; surface_size = cmd->u.surface_create.height * abs(cmd->u.surface_create.stride); - dprint(qxl, 3, "%s: dirty surface %d, offset %d, size %d\n", __func__, - i, (int)surface_offset, surface_size); + trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size); qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size); } } @@ -1545,6 +1606,10 @@ static void display_refresh(struct DisplayState *ds) { if (qxl0->mode == QXL_MODE_VGA) { qemu_spice_display_refresh(&qxl0->ssd); + } else { + qemu_mutex_lock(&qxl0->ssd.lock); + qemu_spice_cursor_refresh_unlocked(&qxl0->ssd); + qemu_mutex_unlock(&qxl0->ssd.lock); } } @@ -1564,18 +1629,28 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb) qxl->vga.vram_size = ram_min_mb * 1024 * 1024; } - /* vram (surfaces, bar 1) */ + /* vram32 (surfaces, 32bit, bar 1) */ + if (qxl->vram32_size_mb != -1) { + qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; + } + if (qxl->vram32_size < 4096) { + qxl->vram32_size = 4096; + } + + /* vram (surfaces, 64bit, bar 4+5) */ if (qxl->vram_size_mb != -1) { qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; } - if (qxl->vram_size < 4096) { - qxl->vram_size = 4096; + if (qxl->vram_size < qxl->vram32_size) { + qxl->vram_size = qxl->vram32_size; } + if (qxl->revision == 1) { + qxl->vram32_size = 4096; qxl->vram_size = 4096; } - qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1); + qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1); qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); } @@ -1600,9 +1675,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) case 2: /* spice 0.6 -- qxl-2 */ pci_device_rev = QXL_REVISION_STABLE_V06; break; -#if SPICE_INTERFACE_QXL_MINOR >= 1 case 3: /* qxl-3 */ -#endif default: pci_device_rev = QXL_DEFAULT_REVISION; break; @@ -1619,6 +1692,8 @@ static int qxl_init_common(PCIQXLDevice *qxl) memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size); vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); + memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar, + 0, qxl->vram32_size); io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); if (qxl->revision == 1) { @@ -1642,7 +1717,29 @@ static int qxl_init_common(PCIQXLDevice *qxl) PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram); pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram_bar); + PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar); + + if (qxl->vram32_size < qxl->vram_size) { + /* + * Make the 64bit vram bar show up only in case it is + * configured to be larger than the 32bit vram bar. + */ + pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, + &qxl->vram_bar); + } + + /* print pci bar details */ + dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", + qxl->id == 0 ? "pri" : "sec", + qxl->vga.vram_size / (1024*1024)); + dprint(qxl, 1, "vram/32: %d MB [region 1]\n", + qxl->vram32_size / (1024*1024)); + dprint(qxl, 1, "vram/64: %d MB %s\n", + qxl->vram_size / (1024*1024), + qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); qxl->ssd.qxl.base.sif = &qxl_interface.base; qxl->ssd.qxl.id = qxl->id; @@ -1652,6 +1749,8 @@ static int qxl_init_common(PCIQXLDevice *qxl) init_pipe_signaling(qxl); qxl_reset_state(qxl); + qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); + return 0; } @@ -1697,7 +1796,7 @@ static void qxl_pre_save(void *opaque) PCIQXLDevice* d = opaque; uint8_t *ram_start = d->vga.vram_ptr; - dprint(d, 1, "%s:\n", __FUNCTION__); + trace_qxl_pre_save(d->id); if (d->last_release == NULL) { d->last_release_offset = 0; } else { @@ -1710,10 +1809,9 @@ static int qxl_pre_load(void *opaque) { PCIQXLDevice* d = opaque; - dprint(d, 1, "%s: start\n", __FUNCTION__); + trace_qxl_pre_load(d->id); qxl_hard_reset(d, 1); qxl_exit_vga_mode(d); - dprint(d, 1, "%s: done\n", __FUNCTION__); return 0; } @@ -1725,7 +1823,6 @@ static void qxl_create_memslots(PCIQXLDevice *d) if (!d->guest_slots[i].active) { continue; } - dprint(d, 1, "%s: restoring guest slot %d\n", __func__, i); qxl_add_memslot(d, i, 0, QXL_SYNC); } } @@ -1737,8 +1834,6 @@ static int qxl_post_load(void *opaque, int version) QXLCommandExt *cmds; int in, out, newmode; - dprint(d, 1, "%s: start\n", __FUNCTION__); - assert(d->last_release_offset < d->vga.vram_size); if (d->last_release_offset == 0) { d->last_release = NULL; @@ -1748,8 +1843,7 @@ static int qxl_post_load(void *opaque, int version) d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); - dprint(d, 1, "%s: restore mode (%s)\n", __FUNCTION__, - qxl_mode_to_string(d->mode)); + trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode)); newmode = d->mode; d->mode = QXL_MODE_UNDEFINED; @@ -1791,8 +1885,6 @@ static int qxl_post_load(void *opaque, int version) qxl_set_mode(d, d->shadow_rom.mode, 1); break; } - dprint(d, 1, "%s: done\n", __FUNCTION__); - return 0; } @@ -1859,7 +1951,7 @@ static VMStateDescription qxl_vmstate = { static Property qxl_properties[] = { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, 64 * 1024 * 1024), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, QXL_DEFAULT_REVISION), @@ -1867,7 +1959,8 @@ static Property qxl_properties[] = { DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1), - DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram_size_mb, -1), + DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, 0), + DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/qxl.h b/hw/qxl.h index d0629916ad..11a0db3f7d 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -16,8 +16,14 @@ enum qxl_mode { QXL_MODE_NATIVE, }; +#ifndef QXL_VRAM64_RANGE_INDEX +#define QXL_VRAM64_RANGE_INDEX 4 +#endif + #define QXL_UNDEFINED_IO UINT32_MAX +#define QXL_NUM_DIRTY_RECTS 64 + typedef struct PCIQXLDevice { PCIDevice pci; SimpleSpiceDisplay ssd; @@ -52,7 +58,7 @@ typedef struct PCIQXLDevice { uint32_t abs_stride; uint32_t bits_pp; uint32_t bytes_pp; - uint8_t *data, *flipped; + uint8_t *data; } guest_primary; struct surfaces { @@ -86,6 +92,8 @@ typedef struct PCIQXLDevice { /* vram pci bar */ uint32_t vram_size; MemoryRegion vram_bar; + uint32_t vram32_size; + MemoryRegion vram32_bar; /* io bar */ MemoryRegion io_bar; @@ -93,6 +101,13 @@ typedef struct PCIQXLDevice { /* user-friendly properties (in megabytes) */ uint32_t ram_size_mb; uint32_t vram_size_mb; + uint32_t vram32_size_mb; + + /* qxl_render_update state */ + int render_update_cookie_num; + int num_dirty_rects; + QXLRect dirty[QXL_NUM_DIRTY_RECTS]; + QEMUBH *update_area_bh; } PCIQXLDevice; #define PANIC_ON(x) if ((x)) { \ @@ -108,11 +123,7 @@ typedef struct PCIQXLDevice { } \ } while (0) -#if SPICE_INTERFACE_QXL_MINOR >= 1 #define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10 -#else -#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06 -#endif /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); @@ -122,7 +133,7 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, uint32_t clear_dirty_region, - qxl_async_io async); + qxl_async_io async, QXLCookie *cookie); void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count); void qxl_spice_oom(PCIQXLDevice *qxl); @@ -138,9 +149,5 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); void qxl_render_resize(PCIQXLDevice *qxl); void qxl_render_update(PCIQXLDevice *qxl); void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext); -#if SPICE_INTERFACE_QXL_MINOR >= 1 -void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id, - struct QXLRect *area, - uint32_t clear_dirty_region, - int is_vga); -#endif +void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie); +void qxl_render_update_area_bh(void *opaque); diff --git a/hw/r2d.c b/hw/r2d.c index c80f9e3a29..c55de0141b 100644 --- a/hw/r2d.c +++ b/hw/r2d.c @@ -192,16 +192,16 @@ static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, } typedef struct ResetData { - CPUState *env; + CPUSH4State *env; uint32_t vector; } ResetData; static void main_cpu_reset(void *opaque) { ResetData *s = (ResetData *)opaque; - CPUState *env = s->env; + CPUSH4State *env = s->env; - cpu_reset(env); + cpu_state_reset(env); env->pc = s->vector; } @@ -224,7 +224,7 @@ static void r2d_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUSH4State *env; ResetData *reset_info; struct SH7750State *s; MemoryRegion *sdram = g_new(MemoryRegion, 1); diff --git a/hw/realview.c b/hw/realview.c index ae1bbcdac3..cf55204c96 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -12,7 +12,6 @@ #include "primecell.h" #include "devices.h" #include "pci.h" -#include "usb-ohci.h" #include "net.h" #include "sysemu.h" #include "boards.h" @@ -129,7 +128,7 @@ static void realview_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model, enum realview_board_type board_type) { - CPUState *env = NULL; + CPUARMState *env = NULL; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram_lo = g_new(MemoryRegion, 1); MemoryRegion *ram_hi = g_new(MemoryRegion, 1); @@ -305,7 +304,7 @@ static void realview_init(ram_addr_t ram_size, sysbus_connect_irq(busdev, 3, pic[51]); pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci"); if (usb_enabled) { - usb_ohci_init_pci(pci_bus, -1); + pci_create_simple(pci_bus, -1, "pci-ohci"); } n = drive_get_max_bus(IF_SCSI); while (n >= 0) { diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index c450e4bb5b..be1f5f1061 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -85,7 +85,7 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) return bus; } -static void s390_virtio_irq(CPUState *env, int config_change, uint64_t token) +static void s390_virtio_irq(CPUS390XState *env, int config_change, uint64_t token) { if (kvm_enabled()) { kvm_s390_virtio_irq(env, config_change, token); @@ -116,7 +116,7 @@ static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev) s390_virtio_device_sync(dev); if (dev->qdev.hotplugged) { - CPUState *env = s390_cpu_addr2state(0); + CPUS390XState *env = s390_cpu_addr2state(0); s390_virtio_irq(env, VIRTIO_PARAM_DEV_ADD, dev->dev_offs); } @@ -331,7 +331,7 @@ static void virtio_s390_notify(void *opaque, uint16_t vector) { VirtIOS390Device *dev = (VirtIOS390Device*)opaque; uint64_t token = s390_virtio_device_vq_token(dev, vector); - CPUState *env = s390_cpu_addr2state(0); + CPUS390XState *env = s390_cpu_addr2state(0); s390_virtio_irq(env, 0, token); } diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c index 51123a7535..1ebe70d0e3 100644 --- a/hw/s390-virtio.c +++ b/hw/s390-virtio.c @@ -61,9 +61,9 @@ #define MAX_BLK_DEVS 10 static VirtIOS390Bus *s390_bus; -static CPUState **ipi_states; +static CPUS390XState **ipi_states; -CPUState *s390_cpu_addr2state(uint16_t cpu_addr) +CPUS390XState *s390_cpu_addr2state(uint16_t cpu_addr) { if (cpu_addr >= smp_cpus) { return NULL; @@ -72,7 +72,7 @@ CPUState *s390_cpu_addr2state(uint16_t cpu_addr) return ipi_states[cpu_addr]; } -int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall) +int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall) { int r = 0, i; @@ -129,7 +129,7 @@ int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall) */ static unsigned s390_running_cpus; -void s390_add_running_cpu(CPUState *env) +void s390_add_running_cpu(CPUS390XState *env) { if (env->halted) { s390_running_cpus++; @@ -138,7 +138,7 @@ void s390_add_running_cpu(CPUState *env) } } -unsigned s390_del_running_cpu(CPUState *env) +unsigned s390_del_running_cpu(CPUS390XState *env) { if (env->halted == 0) { assert(s390_running_cpus >= 1); @@ -157,7 +157,7 @@ static void s390_init(ram_addr_t my_ram_size, const char *initrd_filename, const char *cpu_model) { - CPUState *env = NULL; + CPUS390XState *env = NULL; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); ram_addr_t kernel_size = 0; @@ -205,10 +205,10 @@ static void s390_init(ram_addr_t my_ram_size, cpu_model = "host"; } - ipi_states = g_malloc(sizeof(CPUState *) * smp_cpus); + ipi_states = g_malloc(sizeof(CPUS390XState *) * smp_cpus); for (i = 0; i < smp_cpus; i++) { - CPUState *tmp_env; + CPUS390XState *tmp_env; tmp_env = cpu_init(cpu_model); if (!env) { @@ -224,13 +224,17 @@ static void s390_init(ram_addr_t my_ram_size, s390_add_running_cpu(env); if (kernel_filename) { - kernel_size = load_image(kernel_filename, qemu_get_ram_ptr(0)); - if (lduw_be_phys(KERN_IMAGE_START) != 0x0dd0) { - fprintf(stderr, "Specified image is not an s390 boot image\n"); - exit(1); + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, NULL, + NULL, 1, ELF_MACHINE, 0); + if (kernel_size == -1UL) { + kernel_size = load_image_targphys(kernel_filename, 0, ram_size); } - + /* + * we can not rely on the ELF entry point, since up to 3.2 this + * value was 0x800 (the SALIPL loader) and it wont work. For + * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine. + */ env->psw.addr = KERN_IMAGE_START; env->psw.mask = 0x0000000180000000ULL; } else { @@ -243,7 +247,7 @@ static void s390_init(ram_addr_t my_ram_size, } bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - bios_size = load_image(bios_filename, qemu_get_ram_ptr(ZIPL_LOAD_ADDR)); + bios_size = load_image_targphys(bios_filename, ZIPL_LOAD_ADDR, 4096); g_free(bios_filename); if ((long)bios_size < 0) { @@ -263,15 +267,17 @@ static void s390_init(ram_addr_t my_ram_size, while (kernel_size + 0x100000 > initrd_offset) { initrd_offset += 0x100000; } - initrd_size = load_image(initrd_filename, qemu_get_ram_ptr(initrd_offset)); - - stq_be_phys(INITRD_PARM_START, initrd_offset); - stq_be_phys(INITRD_PARM_SIZE, initrd_size); + initrd_size = load_image_targphys(initrd_filename, initrd_offset, + ram_size - initrd_offset); + /* we have to overwrite values in the kernel image, which are "rom" */ + memcpy(rom_ptr(INITRD_PARM_START), &initrd_offset, 8); + memcpy(rom_ptr(INITRD_PARM_SIZE), &initrd_size, 8); } if (kernel_cmdline) { - cpu_physical_memory_write(KERN_PARM_AREA, kernel_cmdline, - strlen(kernel_cmdline) + 1); + /* we have to overwrite values in the kernel image, which are "rom" */ + memcpy(rom_ptr(KERN_PARM_AREA), kernel_cmdline, + strlen(kernel_cmdline) + 1); } /* Create VirtIO network adapters */ diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 2cb5a18da2..8e76c5d32c 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -7,6 +7,7 @@ #include "trace.h" #include "dma.h" +static char *scsibus_get_dev_path(DeviceState *dev); static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); static void scsi_req_dequeue(SCSIRequest *req); @@ -14,6 +15,7 @@ static void scsi_req_dequeue(SCSIRequest *req); static struct BusInfo scsi_bus_info = { .name = "SCSI", .size = sizeof(SCSIBus), + .get_dev_path = scsibus_get_dev_path, .get_fw_dev_path = scsibus_get_fw_dev_path, .props = (Property[]) { DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), @@ -1423,6 +1425,22 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) sdev->unit_attention = sense; } +static char *scsibus_get_dev_path(DeviceState *dev) +{ + SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); + DeviceState *hba = dev->parent_bus->parent; + char *id = NULL; + + if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) { + id = hba->parent_bus->info->get_dev_path(hba); + } + if (id) { + return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); + } else { + return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); + } +} + static char *scsibus_get_fw_dev_path(DeviceState *dev) { SCSIDevice *d = SCSI_DEVICE(dev); diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index add399e97b..9949786e52 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -471,8 +471,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) case 0x83: /* Device identification page, mandatory */ { - int max_len = 255 - 8; - int id_len = strlen(bdrv_get_device_name(s->qdev.conf.bs)); + const char *str = s->serial ?: bdrv_get_device_name(s->qdev.conf.bs); + int max_len = s->serial ? 20 : 255 - 8; + int id_len = strlen(str); if (id_len > max_len) { id_len = max_len; @@ -486,7 +487,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) outbuf[buflen++] = 0; // reserved outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, bdrv_get_device_name(s->qdev.conf.bs), id_len); + memcpy(outbuf+buflen, str, id_len); buflen += id_len; break; } @@ -1152,9 +1153,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) outbuf = r->iov.iov_base; switch (req->cmd.buf[0]) { case TEST_UNIT_READY: - if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { - goto not_ready; - } + assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs)); break; case INQUIRY: buflen = scsi_disk_emulate_inquiry(req, outbuf); @@ -1209,7 +1208,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) memset(outbuf, 0, 8); bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { - goto not_ready; + scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); + return -1; } if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { goto illegal_request; @@ -1269,7 +1269,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) memset(outbuf, 0, req->cmd.xfer); bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { - goto not_ready; + scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); + return -1; } if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { goto illegal_request; @@ -1314,14 +1315,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) buflen = MIN(buflen, req->cmd.xfer); return buflen; -not_ready: - if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - } else { - scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - } - return -1; - illegal_request: if (r->req.status == -1) { scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); @@ -1356,6 +1349,30 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) #endif switch (command) { + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + case RESERVE: + case RESERVE_10: + case RELEASE: + case RELEASE_10: + case START_STOP: + case ALLOW_MEDIUM_REMOVAL: + case GET_CONFIGURATION: + case GET_EVENT_STATUS_NOTIFICATION: + case MECHANISM_STATUS: + case REQUEST_SENSE: + break; + + default: + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + return 0; + } + break; + } + + switch (command) { case TEST_UNIT_READY: case INQUIRY: case MODE_SENSE: diff --git a/hw/sh.h b/hw/sh.h index 6488db25c4..40df18c5dc 100644 --- a/hw/sh.h +++ b/hw/sh.h @@ -11,7 +11,7 @@ struct SH7750State; struct MemoryRegion; -struct SH7750State *sh7750_init(CPUState * cpu, struct MemoryRegion *sysmem); +struct SH7750State *sh7750_init(CPUSH4State * cpu, struct MemoryRegion *sysmem); typedef struct { /* The callback will be triggered if any of the designated lines change */ diff --git a/hw/sh7750.c b/hw/sh7750.c index 4f4d8e7d05..e7129283d1 100644 --- a/hw/sh7750.c +++ b/hw/sh7750.c @@ -712,7 +712,7 @@ static void sh7750_mmct_write(void *opaque, target_phys_addr_t addr, } } -static const struct MemoryRegionOps sh7750_mmct_ops = { +static const MemoryRegionOps sh7750_mmct_ops = { .read = sh7750_mmct_read, .write = sh7750_mmct_write, .endianness = DEVICE_NATIVE_ENDIAN, diff --git a/hw/sh_intc.c b/hw/sh_intc.c index b24ec77582..7d31ced858 100644 --- a/hw/sh_intc.c +++ b/hw/sh_intc.c @@ -283,7 +283,7 @@ static void sh_intc_write(void *opaque, target_phys_addr_t offset, #endif } -static const struct MemoryRegionOps sh_intc_ops = { +static const MemoryRegionOps sh_intc_ops = { .read = sh_intc_read, .write = sh_intc_write, .endianness = DEVICE_NATIVE_ENDIAN, diff --git a/hw/shix.c b/hw/shix.c index e259c17c52..dd9ce174f9 100644 --- a/hw/shix.c +++ b/hw/shix.c @@ -43,7 +43,7 @@ static void shix_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { int ret; - CPUState *env; + CPUSH4State *env; struct SH7750State *s; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *rom = g_new(MemoryRegion, 1); diff --git a/hw/spapr.c b/hw/spapr.c index dffb6a2a50..bfaf260d54 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -83,7 +83,8 @@ sPAPREnvironment *spapr; -qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num) +qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, + enum xics_irq_type type) { uint32_t irq; qemu_irq qirq; @@ -95,7 +96,7 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num) irq = spapr->next_irq++; } - qirq = xics_find_qirq(spapr->icp, irq); + qirq = xics_assign_irq(spapr->icp, irq, type); if (!qirq) { return NULL; } @@ -110,7 +111,7 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num) static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr) { int ret = 0, offset; - CPUState *env; + CPUPPCState *env; char cpu_model[32]; int smt = kvmppc_smt_threads(); @@ -155,7 +156,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, long hash_shift) { void *fdt; - CPUState *env; + CPUPPCState *env; uint64_t mem_reg_property[2]; uint32_t start_prop = cpu_to_be32(initrd_base); uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); @@ -476,7 +477,7 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; } -static void emulate_spapr_hypercall(CPUState *env) +static void emulate_spapr_hypercall(CPUPPCState *env) { env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]); } @@ -502,6 +503,13 @@ static void spapr_reset(void *opaque) } +static void spapr_cpu_reset(void *opaque) +{ + CPUPPCState *env = opaque; + + cpu_state_reset(env); +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(ram_addr_t ram_size, const char *boot_device, @@ -510,7 +518,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUPPCState *env; int i; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -560,7 +568,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, } /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); - qemu_register_reset((QEMUResetHandler *)&cpu_reset, env); + qemu_register_reset(spapr_cpu_reset, env); env->hreset_vector = 0x60; env->hreset_excp_prefix = 0; diff --git a/hw/spapr.h b/hw/spapr.h index e946a3433e..11160b02da 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -278,15 +278,26 @@ extern sPAPREnvironment *spapr; do { } while (0) #endif -typedef target_ulong (*spapr_hcall_fn)(CPUState *env, sPAPREnvironment *spapr, +typedef target_ulong (*spapr_hcall_fn)(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args); void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); -target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, +target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode, target_ulong *args); -qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num); +qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, + enum xics_irq_type type); + +static inline qemu_irq spapr_allocate_msi(uint32_t hint, uint32_t *irq_num) +{ + return spapr_allocate_irq(hint, irq_num, XICS_MSI); +} + +static inline qemu_irq spapr_allocate_lsi(uint32_t hint, uint32_t *irq_num) +{ + return spapr_allocate_irq(hint, irq_num, XICS_LSI); +} static inline uint32_t rtas_ld(target_ulong phys, int n) { diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 6ac7384013..634763eefd 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -92,7 +92,7 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, return rb; } -static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_enter(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; @@ -181,7 +181,7 @@ enum { REMOVE_HW = 3, }; -static target_ulong remove_hpte(CPUState *env, target_ulong ptex, +static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong avpn, target_ulong flags, target_ulong *vp, target_ulong *rp) @@ -219,7 +219,7 @@ static target_ulong remove_hpte(CPUState *env, target_ulong ptex, return REMOVE_SUCCESS; } -static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_remove(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; @@ -265,7 +265,7 @@ static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, #define H_BULK_REMOVE_MAX_BATCH 4 -static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_bulk_remove(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { int i; @@ -311,7 +311,7 @@ static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_protect(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; @@ -356,7 +356,7 @@ static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_set_dabr(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { /* FIXME: actually implement this */ @@ -375,7 +375,7 @@ static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr, #define VPA_SHARED_PROC_OFFSET 0x9 #define VPA_SHARED_PROC_VAL 0x2 -static target_ulong register_vpa(CPUState *env, target_ulong vpa) +static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) { uint16_t size; uint8_t tmp; @@ -410,7 +410,7 @@ static target_ulong register_vpa(CPUState *env, target_ulong vpa) return H_SUCCESS; } -static target_ulong deregister_vpa(CPUState *env, target_ulong vpa) +static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa) { if (env->slb_shadow) { return H_RESOURCE; @@ -424,7 +424,7 @@ static target_ulong deregister_vpa(CPUState *env, target_ulong vpa) return H_SUCCESS; } -static target_ulong register_slb_shadow(CPUState *env, target_ulong addr) +static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) { uint32_t size; @@ -451,13 +451,13 @@ static target_ulong register_slb_shadow(CPUState *env, target_ulong addr) return H_SUCCESS; } -static target_ulong deregister_slb_shadow(CPUState *env, target_ulong addr) +static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr) { env->slb_shadow = 0; return H_SUCCESS; } -static target_ulong register_dtl(CPUState *env, target_ulong addr) +static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) { uint32_t size; @@ -482,7 +482,7 @@ static target_ulong register_dtl(CPUState *env, target_ulong addr) return H_SUCCESS; } -static target_ulong deregister_dtl(CPUState *emv, target_ulong addr) +static target_ulong deregister_dtl(CPUPPCState *emv, target_ulong addr) { env->dispatch_trace_log = 0; env->dtl_size = 0; @@ -490,14 +490,14 @@ static target_ulong deregister_dtl(CPUState *emv, target_ulong addr) return H_SUCCESS; } -static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_register_vpa(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; target_ulong procno = args[1]; target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; - CPUState *tenv; + CPUPPCState *tenv; for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) { if (tenv->cpu_index == procno) { @@ -538,7 +538,7 @@ static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr, return ret; } -static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_cede(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { env->msr |= (1ULL << MSR_EE); @@ -549,7 +549,7 @@ static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_rtas(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong rtas_r3 = args[0]; @@ -561,7 +561,7 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, nret, rtas_r3 + 12 + 4*nargs); } -static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_logical_load(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong size = args[0]; @@ -584,7 +584,7 @@ static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr, return H_PARAMETER; } -static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_logical_store(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong size = args[0]; @@ -608,14 +608,14 @@ static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr, return H_PARAMETER; } -static target_ulong h_logical_icbi(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_logical_icbi(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { /* Nothing to do on emulation, KVM will trap this in the kernel */ return H_SUCCESS; } -static target_ulong h_logical_dcbf(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_logical_dcbf(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { /* Nothing to do on emulation, KVM will trap this in the kernel */ @@ -644,7 +644,7 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) *slot = fn; } -target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, +target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode, target_ulong *args) { if (msr_pr) { diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index 77d40479c8..cfc777804b 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -254,7 +254,7 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, return 0; } -static target_ulong h_register_logical_lan(CPUState *env, +static target_ulong h_register_logical_lan(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) @@ -320,7 +320,7 @@ static target_ulong h_register_logical_lan(CPUState *env, } -static target_ulong h_free_logical_lan(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_free_logical_lan(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; @@ -343,7 +343,7 @@ static target_ulong h_free_logical_lan(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_add_logical_lan_buffer(CPUState *env, +static target_ulong h_add_logical_lan_buffer(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) @@ -392,7 +392,7 @@ static target_ulong h_add_logical_lan_buffer(CPUState *env, return H_SUCCESS; } -static target_ulong h_send_logical_lan(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_send_logical_lan(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; @@ -461,7 +461,7 @@ static target_ulong h_send_logical_lan(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_multicast_ctrl(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_multicast_ctrl(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index cfdd9ddd41..e7ef551c1c 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -32,13 +32,6 @@ #include "hw/pci_internals.h" -static const uint32_t bars[] = { - PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, - PCI_BASE_ADDRESS_2, PCI_BASE_ADDRESS_3, - PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5 - /*, PCI_ROM_ADDRESS*/ -}; - static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid, uint32_t config_addr) { @@ -187,69 +180,6 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(phb->lsi_table[irq_num].qirq, level); } -static int spapr_phb_init(SysBusDevice *s) -{ - sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s); - int i; - - /* Initialize the LSI table */ - for (i = 0; i < SPAPR_PCI_NUM_LSI; i++) { - qemu_irq qirq; - uint32_t num; - - qirq = spapr_allocate_irq(0, &num); - if (!qirq) { - return -1; - } - - phb->lsi_table[i].dt_irq = num; - phb->lsi_table[i].qirq = qirq; - } - - return 0; -} - -static int spapr_main_pci_host_init(PCIDevice *d) -{ - return 0; -} - -static void spapr_main_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = spapr_main_pci_host_init; -} - -static TypeInfo spapr_main_pci_host_info = { - .name = "spapr-pci-host-bridge-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = spapr_main_pci_host_class_init, -}; - -static void spapr_phb_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = spapr_phb_init; -} - -static TypeInfo spapr_phb_info = { - .name = "spapr-pci-host-bridge", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(sPAPRPHBState), - .class_init = spapr_phb_class_init, -}; - -static void spapr_register_types(void) -{ - type_register_static(&spapr_phb_info); - type_register_static(&spapr_main_pci_host_info); -} - -type_init(spapr_register_types) - static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr, unsigned size) { @@ -281,41 +211,35 @@ static void spapr_io_write(void *opaque, target_phys_addr_t addr, assert(0); } -static MemoryRegionOps spapr_io_ops = { +static const MemoryRegionOps spapr_io_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .read = spapr_io_read, .write = spapr_io_write }; -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) +/* + * PHB PCI device + */ +static int spapr_phb_init(SysBusDevice *s) { - DeviceState *dev; - SysBusDevice *s; - sPAPRPHBState *phb; + sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s); + char *namebuf; + int i; PCIBus *bus; - char namebuf[strlen(busname)+11]; - - dev = qdev_create(NULL, "spapr-pci-host-bridge"); - qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); - phb = FROM_SYSBUS(sPAPRPHBState, s); - phb->mem_win_addr = mem_win_addr; + phb->dtbusname = g_strdup_printf("pci@%" PRIx64, phb->buid); + namebuf = alloca(strlen(phb->dtbusname) + 32); - sprintf(namebuf, "%s-mem", busname); + /* Initialize memory regions */ + sprintf(namebuf, "%s.mmio", phb->dtbusname); memory_region_init(&phb->memspace, namebuf, INT64_MAX); - sprintf(namebuf, "%s-memwindow", busname); + sprintf(namebuf, "%s.mmio-alias", phb->dtbusname); memory_region_init_alias(&phb->memwindow, namebuf, &phb->memspace, - SPAPR_PCI_MEM_WIN_BUS_OFFSET, mem_win_size); - memory_region_add_subregion(get_system_memory(), mem_win_addr, + SPAPR_PCI_MEM_WIN_BUS_OFFSET, phb->mem_win_size); + memory_region_add_subregion(get_system_memory(), phb->mem_win_addr, &phb->memwindow); - phb->io_win_addr = io_win_addr; - /* 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 * memory region direction in the system memory space. However, @@ -324,33 +248,92 @@ void spapr_create_phb(sPAPREnvironment *spapr, * 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", busname); + sprintf(namebuf, "%s.io", phb->dtbusname); memory_region_init(&phb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE); /* FIXME: fix to support multiple PHBs */ memory_region_add_subregion(get_system_io(), 0, &phb->iospace); - sprintf(namebuf, "%s-iowindow", busname); + sprintf(namebuf, "%s.io-alias", phb->dtbusname); memory_region_init_io(&phb->iowindow, &spapr_io_ops, phb, namebuf, SPAPR_PCI_IO_WIN_SIZE); - memory_region_add_subregion(get_system_memory(), io_win_addr, + memory_region_add_subregion(get_system_memory(), phb->io_win_addr, &phb->iowindow); - phb->host_state.bus = bus = pci_register_bus(&phb->busdev.qdev, busname, - pci_spapr_set_irq, - pci_spapr_map_irq, - phb, - &phb->memspace, &phb->iospace, - PCI_DEVFN(0, 0), - SPAPR_PCI_NUM_LSI); + 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, + PCI_DEVFN(0, 0), SPAPR_PCI_NUM_LSI); + phb->host_state.bus = bus; + + QLIST_INSERT_HEAD(&spapr->phbs, phb, list); + + /* Initialize the LSI table */ + for (i = 0; i < SPAPR_PCI_NUM_LSI; i++) { + qemu_irq qirq; + uint32_t num; + + qirq = spapr_allocate_lsi(0, &num); + if (!qirq) { + return -1; + } + + phb->lsi_table[i].dt_irq = num; + phb->lsi_table[i].qirq = qirq; + } + + return 0; +} + +static Property spapr_phb_properties[] = { + DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, 0), + DEFINE_PROP_STRING("busname", sPAPRPHBState, busname), + DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, 0), + 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_END_OF_LIST(), +}; + +static void spapr_phb_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + 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); +} - QLIST_INSERT_HEAD(&spapr->phbs, phb, list); +static TypeInfo spapr_phb_info = { + .name = "spapr-pci-host-bridge", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(sPAPRPHBState), + .class_init = spapr_phb_class_init, +}; + +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) +{ + DeviceState *dev; + + dev = qdev_create(NULL, spapr_phb_info.name); - /* pci_bus_set_mem_base(bus, mem_va_start - SPAPR_PCI_MEM_BAR_START); */ + if (busname) { + qdev_prop_set_string(dev, "busname", g_strdup(busname)); + } + qdev_prop_set_uint64(dev, "buid", buid); + 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_init_nofail(dev); } /* Macros to operate with address in OF binding to PCI */ @@ -442,3 +425,9 @@ int spapr_populate_pci_devices(sPAPRPHBState *phb, return 0; } + +static void register_types(void) +{ + type_register_static(&spapr_phb_info); +} +type_init(register_types) diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 213340c915..039f85bd4b 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -33,9 +33,11 @@ typedef struct sPAPRPHBState { PCIHostState host_state; uint64_t buid; + char *busname; + char *dtbusname; MemoryRegion memspace, iospace; - target_phys_addr_t mem_win_addr, io_win_addr; + target_phys_addr_t mem_win_addr, mem_win_size, io_win_addr, io_win_size; MemoryRegion memwindow, iowindow; struct { diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c index c0723b3039..09465853ba 100644 --- a/hw/spapr_rtas.c +++ b/hw/spapr_rtas.c @@ -118,7 +118,7 @@ static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { target_ulong id; - CPUState *env; + CPUPPCState *env; if (nargs != 1 || nret != 2) { rtas_st(rets, 0, -3); @@ -151,7 +151,7 @@ static void rtas_start_cpu(sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { target_ulong id, start, r3; - CPUState *env; + CPUPPCState *env; if (nargs != 3 || nret != 1) { rtas_st(rets, 0, -3); diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index ea317efbe4..dbf5a9017e 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -194,7 +194,7 @@ static void rtce_init(VIOsPAPRDevice *dev) } } -static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_put_tce(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong liobn = args[0]; @@ -405,7 +405,7 @@ uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr) /* * CRQ handling */ -static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_reg_crq(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; @@ -453,7 +453,7 @@ static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_free_crq(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_free_crq(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; @@ -474,7 +474,7 @@ static target_ulong h_free_crq(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_send_crq(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_send_crq(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; @@ -498,7 +498,7 @@ static target_ulong h_send_crq(CPUState *env, sPAPREnvironment *spapr, return H_HARDWARE; } -static target_ulong h_enable_crq(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_enable_crq(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; @@ -670,7 +670,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev) dev->qdev.id = id; } - dev->qirq = spapr_allocate_irq(dev->vio_irq_num, &dev->vio_irq_num); + dev->qirq = spapr_allocate_msi(dev->vio_irq_num, &dev->vio_irq_num); if (!dev->qirq) { return -1; } @@ -680,7 +680,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev) return pc->init(dev); } -static target_ulong h_vio_signal(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_vio_signal(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index 3efe24211e..60e22b1600 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -72,7 +72,7 @@ static int spapr_vty_init(VIOsPAPRDevice *sdev) /* Forward declaration */ static VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg); -static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_put_term_char(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; @@ -99,7 +99,7 @@ static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_get_term_char(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; diff --git a/hw/strongarm.h b/hw/strongarm.h index 684f61bee3..02acac3db1 100644 --- a/hw/strongarm.h +++ b/hw/strongarm.h @@ -53,7 +53,7 @@ enum { }; typedef struct { - CPUState *env; + CPUARMState *env; MemoryRegion sdram; DeviceState *pic; DeviceState *gpio; diff --git a/hw/sun4m.c b/hw/sun4m.c index 99fb219b3a..7bcbf37e98 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -228,7 +228,7 @@ void sun4m_irq_info(Monitor *mon) slavio_irq_info(mon, slavio_intctl); } -void cpu_check_irqs(CPUState *env) +void cpu_check_irqs(CPUSPARCState *env) { if (env->pil_in && (env->interrupt_index == 0 || (env->interrupt_index & ~15) == TT_EXTINT)) { @@ -253,7 +253,7 @@ void cpu_check_irqs(CPUState *env) } } -static void cpu_kick_irq(CPUState *env) +static void cpu_kick_irq(CPUSPARCState *env) { env->halted = 0; cpu_check_irqs(env); @@ -262,7 +262,7 @@ static void cpu_kick_irq(CPUState *env) static void cpu_set_irq(void *opaque, int irq, int level) { - CPUState *env = opaque; + CPUSPARCState *env = opaque; if (level) { trace_sun4m_cpu_set_irq_raise(irq); @@ -281,17 +281,17 @@ static void dummy_cpu_set_irq(void *opaque, int irq, int level) static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUSPARCState *env = opaque; - cpu_reset(env); + cpu_state_reset(env); env->halted = 0; } static void secondary_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUSPARCState *env = opaque; - cpu_reset(env); + cpu_state_reset(env); env->halted = 1; } @@ -809,7 +809,7 @@ static TypeInfo ram_info = { static void cpu_devinit(const char *cpu_model, unsigned int id, uint64_t prom_addr, qemu_irq **cpu_irqs) { - CPUState *env; + CPUSPARCState *env; env = cpu_init(cpu_model); if (!env) { diff --git a/hw/sun4u.c b/hw/sun4u.c index 423108f236..237e20c1bf 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -81,7 +81,7 @@ #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01) #define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02) -#define MAX_PILS 16 +#define IVEC_MAX 0x30 #define TICK_MAX 0x7fffffffffffffffULL @@ -243,7 +243,7 @@ static unsigned long sun4u_load_kernel(const char *kernel_filename, return kernel_size; } -void cpu_check_irqs(CPUState *env) +void cpu_check_irqs(CPUSPARCState *env) { uint32_t pil = env->pil_in | (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); @@ -297,30 +297,36 @@ void cpu_check_irqs(CPUState *env) } } -static void cpu_kick_irq(CPUState *env) +static void cpu_kick_irq(CPUSPARCState *env) { env->halted = 0; cpu_check_irqs(env); qemu_cpu_kick(env); } -static void cpu_set_irq(void *opaque, int irq, int level) +static void cpu_set_ivec_irq(void *opaque, int irq, int level) { - CPUState *env = opaque; + CPUSPARCState *env = opaque; if (level) { - CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq); - env->pil_in |= 1 << irq; - cpu_kick_irq(env); - } else { - CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq); - env->pil_in &= ~(1 << irq); - cpu_check_irqs(env); + CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq); + env->interrupt_index = TT_IVEC; + env->pil_in |= 1 << 5; + env->ivec_status |= 0x20; + env->ivec_data[0] = (0x1f << 6) | irq; + env->ivec_data[1] = 0; + env->ivec_data[2] = 0; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq); + env->pil_in &= ~(1 << 5); + env->ivec_status &= ~0x20; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); } } typedef struct ResetData { - CPUState *env; + CPUSPARCState *env; uint64_t prom_addr; } ResetData; @@ -344,7 +350,7 @@ void cpu_get_timer(QEMUFile *f, CPUTimer *s) qemu_get_timer(f, s->qtimer); } -static CPUTimer* cpu_timer_create(const char* name, CPUState *env, +static CPUTimer* cpu_timer_create(const char* name, CPUSPARCState *env, QEMUBHFunc *cb, uint32_t frequency, uint64_t disabled_mask) { @@ -373,10 +379,10 @@ static void cpu_timer_reset(CPUTimer *timer) static void main_cpu_reset(void *opaque) { ResetData *s = (ResetData *)opaque; - CPUState *env = s->env; + CPUSPARCState *env = s->env; static unsigned int nr_resets; - cpu_reset(env); + cpu_state_reset(env); cpu_timer_reset(env->tick); cpu_timer_reset(env->stick); @@ -396,7 +402,7 @@ static void main_cpu_reset(void *opaque) static void tick_irq(void *opaque) { - CPUState *env = opaque; + CPUSPARCState *env = opaque; CPUTimer* timer = env->tick; @@ -413,7 +419,7 @@ static void tick_irq(void *opaque) static void stick_irq(void *opaque) { - CPUState *env = opaque; + CPUSPARCState *env = opaque; CPUTimer* timer = env->stick; @@ -430,7 +436,7 @@ static void stick_irq(void *opaque) static void hstick_irq(void *opaque) { - CPUState *env = opaque; + CPUSPARCState *env = opaque; CPUTimer* timer = env->hstick; @@ -521,13 +527,29 @@ void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit) } } -static void dummy_isa_irq_handler(void *opaque, int n, int level) +static void isa_irq_handler(void *opaque, int n, int level) { + static const int isa_irq_to_ivec[16] = { + [1] = 0x29, /* keyboard */ + [4] = 0x2b, /* serial */ + [6] = 0x27, /* floppy */ + [7] = 0x22, /* parallel */ + [12] = 0x2a, /* mouse */ + }; + qemu_irq *irqs = opaque; + int ivec; + + assert(n < 16); + ivec = isa_irq_to_ivec[n]; + EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec); + if (ivec) { + qemu_set_irq(irqs[ivec], level); + } } /* EBUS (Eight bit bus) bridge */ static ISABus * -pci_ebus_init(PCIBus *bus, int devfn) +pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs) { qemu_irq *isa_irq; PCIDevice *pci_dev; @@ -536,7 +558,7 @@ pci_ebus_init(PCIBus *bus, int devfn) pci_dev = pci_create_simple(bus, devfn, "ebus"); isa_bus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(&pci_dev->qdev, "isa.0")); - isa_irq = qemu_allocate_irqs(dummy_isa_irq_handler, NULL, 16); + isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16); isa_bus_irqs(isa_bus, isa_irq); return isa_bus; } @@ -714,9 +736,9 @@ static TypeInfo ram_info = { .class_init = ram_class_init, }; -static CPUState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef) +static CPUSPARCState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef) { - CPUState *env; + CPUSPARCState *env; ResetData *reset_info; uint32_t tick_frequency = 100*1000000; @@ -755,13 +777,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem, const char *initrd_filename, const char *cpu_model, const struct hwdef *hwdef) { - CPUState *env; + CPUSPARCState *env; M48t59State *nvram; unsigned int i; long initrd_size, kernel_size; PCIBus *pci_bus, *pci_bus2, *pci_bus3; ISABus *isa_bus; - qemu_irq *irq; + qemu_irq *ivec_irqs, *pbm_irqs; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *fd[MAX_FD]; void *fw_cfg; @@ -774,14 +796,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem, prom_init(hwdef->prom_addr, bios_name); - - irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS); - pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq, &pci_bus2, - &pci_bus3); + ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, env, IVEC_MAX); + pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs, &pci_bus2, + &pci_bus3, &pbm_irqs); pci_vga_init(pci_bus); // XXX Should be pci_bus3 - isa_bus = pci_ebus_init(pci_bus, -1); + isa_bus = pci_ebus_init(pci_bus, -1, pbm_irqs); i = 0; if (hwdef->console_serial_base) { diff --git a/hw/usb-ohci.h b/hw/usb-ohci.h deleted file mode 100644 index eefcef3602..0000000000 --- a/hw/usb-ohci.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef QEMU_USB_OHCI_H -#define QEMU_USB_OHCI_H - -#include "qemu-common.h" - -void usb_ohci_init_pci(struct PCIBus *bus, int devfn); - -#endif - diff --git a/hw/usb-uhci.h b/hw/usb-uhci.h deleted file mode 100644 index 3e4d3777d4..0000000000 --- a/hw/usb-uhci.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef QEMU_USB_UHCI_H -#define QEMU_USB_UHCI_H - -#include "qemu-common.h" - -void usb_uhci_piix3_init(PCIBus *bus, int devfn); -void usb_uhci_piix4_init(PCIBus *bus, int devfn); -void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn); - -#endif diff --git a/hw/usb.h b/hw/usb.h index 4470ea890a..e95085f0b3 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -39,11 +39,12 @@ #define USB_TOKEN_IN 0x69 /* device -> host */ #define USB_TOKEN_OUT 0xe1 /* host -> device */ -#define USB_RET_NODEV (-1) -#define USB_RET_NAK (-2) -#define USB_RET_STALL (-3) -#define USB_RET_BABBLE (-4) -#define USB_RET_ASYNC (-5) +#define USB_RET_NODEV (-1) +#define USB_RET_NAK (-2) +#define USB_RET_STALL (-3) +#define USB_RET_BABBLE (-4) +#define USB_RET_IOERROR (-5) +#define USB_RET_ASYNC (-6) #define USB_SPEED_LOW 0 #define USB_SPEED_FULL 1 @@ -176,6 +177,7 @@ struct USBEndpoint { uint8_t type; uint8_t ifnum; int max_packet_size; + bool pipeline; USBDevice *dev; QTAILQ_HEAD(, USBPacket) queue; }; @@ -325,6 +327,7 @@ struct USBPacket { int pid; USBEndpoint *ep; QEMUIOVector iov; + uint64_t parameter; /* control transfers */ int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBPacketState state; @@ -333,6 +336,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_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); @@ -363,6 +367,7 @@ void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum); void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, uint16_t raw); int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); +void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); void usb_attach(USBPort *port); void usb_detach(USBPort *port); @@ -373,12 +378,12 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); /* usb-linux.c */ -USBDevice *usb_host_device_open(const char *devname); +USBDevice *usb_host_device_open(USBBus *bus, const char *devname); int usb_host_device_close(const char *devname); void usb_host_info(Monitor *mon); /* usb-bt.c */ -USBDevice *usb_bt_init(HCIInfo *hci); +USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci); /* usb ports of the VM */ @@ -431,7 +436,8 @@ struct USBBusOps { void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); USBBus *usb_bus_find(int busnr); void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)); + USBDevice *(*usbdevice_init)(USBBus *bus, + const char *params)); USBDevice *usb_create(USBBus *bus, const char *name); USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); diff --git a/hw/usb-bus.c b/hw/usb/bus.c index ae79a4527b..d3f835895d 100644 --- a/hw/usb-bus.c +++ b/hw/usb/bus.c @@ -1,6 +1,6 @@ -#include "hw.h" -#include "usb.h" -#include "qdev.h" +#include "hw/hw.h" +#include "hw/usb.h" +#include "hw/qdev.h" #include "sysemu.h" #include "monitor.h" #include "trace.h" @@ -203,13 +203,14 @@ typedef struct LegacyUSBFactory { const char *name; const char *usbdevice_name; - USBDevice *(*usbdevice_init)(const char *params); + USBDevice *(*usbdevice_init)(USBBus *bus, const char *params); } LegacyUSBFactory; static GSList *legacy_usb_factory; void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)) + USBDevice *(*usbdevice_init)(USBBus *bus, + const char *params)) { if (usbdevice_name) { LegacyUSBFactory *f = g_malloc0(sizeof(*f)); @@ -224,17 +225,6 @@ USBDevice *usb_create(USBBus *bus, const char *name) { DeviceState *dev; -#if 1 - /* temporary stopgap until all usb is properly qdev-ified */ - if (!bus) { - bus = usb_bus_find(-1); - if (!bus) - return NULL; - error_report("%s: no bus specified, using \"%s\" for \"%s\"", - __FUNCTION__, bus->qbus.name, name); - } -#endif - dev = qdev_create(&bus->qbus, name); return USB_DEVICE(dev); } @@ -565,7 +555,7 @@ USBDevice *usbdevice_create(const char *cmdline) } return usb_create_simple(bus, f->name); } - return f->usbdevice_init(params); + return f->usbdevice_init(bus, params); } static void usb_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb.c b/hw/usb/core.c index e5b8f335dc..a4048fe3e0 100644 --- a/hw/usb.c +++ b/hw/usb/core.c @@ -24,8 +24,9 @@ * THE SOFTWARE. */ #include "qemu-common.h" -#include "usb.h" +#include "hw/usb.h" #include "iov.h" +#include "trace.h" void usb_attach(USBPort *port) { @@ -94,6 +95,7 @@ void usb_wakeup(USBEndpoint *ep) #define SETUP_STATE_SETUP 1 #define SETUP_STATE_DATA 2 #define SETUP_STATE_ACK 3 +#define SETUP_STATE_PARAM 4 static int do_token_setup(USBDevice *s, USBPacket *p) { @@ -225,6 +227,50 @@ static int do_token_out(USBDevice *s, USBPacket *p) } } +static int do_parameter(USBDevice *s, USBPacket *p) +{ + int request, value, index; + int i, ret = 0; + + for (i = 0; i < 8; i++) { + s->setup_buf[i] = p->parameter >> (i*8); + } + + s->setup_state = SETUP_STATE_PARAM; + s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; + s->setup_index = 0; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; + index = (s->setup_buf[5] << 8) | s->setup_buf[4]; + + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + return USB_RET_STALL; + } + + if (p->pid == USB_TOKEN_OUT) { + usb_packet_copy(p, s->data_buf, s->setup_len); + } + + ret = usb_device_handle_control(s, p, request, value, index, + s->setup_len, s->data_buf); + if (ret < 0) { + return ret; + } + + if (ret < s->setup_len) { + s->setup_len = ret; + } + if (p->pid == USB_TOKEN_IN) { + usb_packet_copy(p, s->data_buf, s->setup_len); + } + + return ret; +} + /* ctrl complete function for devices which use usb_generic_handle_packet and may return USB_RET_ASYNC from their handle_control callback. Device code which does this *must* call this function instead of the normal @@ -249,6 +295,16 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) p->result = 0; break; + case SETUP_STATE_PARAM: + if (p->result < s->setup_len) { + s->setup_len = p->result; + } + if (p->pid == USB_TOKEN_IN) { + p->result = 0; + usb_packet_copy(p, s->data_buf, s->setup_len); + } + break; + default: break; } @@ -291,6 +347,9 @@ static int usb_process_one(USBPacket *p) if (p->ep->nr == 0) { /* control pipe */ + if (p->parameter) { + return do_parameter(dev, p); + } switch (p->pid) { case USB_TOKEN_SETUP: return do_token_setup(dev, p); @@ -319,10 +378,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) } assert(dev == p->ep->dev); assert(dev->state == USB_STATE_DEFAULT); - assert(p->state == USB_PACKET_SETUP); + usb_packet_check_state(p, USB_PACKET_SETUP); assert(p->ep != NULL); - if (QTAILQ_EMPTY(&p->ep->queue)) { + 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); @@ -347,7 +406,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) USBEndpoint *ep = p->ep; int ret; - assert(p->state == USB_PACKET_ASYNC); + 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); @@ -355,7 +414,10 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) while (!QTAILQ_EMPTY(&ep->queue)) { p = QTAILQ_FIRST(&ep->queue); - assert(p->state == USB_PACKET_QUEUED); + if (p->state == USB_PACKET_ASYNC) { + break; + } + usb_packet_check_state(p, USB_PACKET_QUEUED); ret = usb_process_one(p); if (ret == USB_RET_ASYNC) { usb_packet_set_state(p, USB_PACKET_ASYNC); @@ -388,9 +450,8 @@ void usb_packet_init(USBPacket *p) qemu_iovec_init(&p->iov, 1); } -void usb_packet_set_state(USBPacket *p, USBPacketState state) +static const char *usb_packet_state_name(USBPacketState state) { -#ifdef DEBUG static const char *name[] = { [USB_PACKET_UNDEFINED] = "undef", [USB_PACKET_SETUP] = "setup", @@ -399,28 +460,36 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state) [USB_PACKET_COMPLETE] = "complete", [USB_PACKET_CANCELED] = "canceled", }; - static const char *rets[] = { - [-USB_RET_NODEV] = "NODEV", - [-USB_RET_NAK] = "NAK", - [-USB_RET_STALL] = "STALL", - [-USB_RET_BABBLE] = "BABBLE", - [-USB_RET_ASYNC] = "ASYNC", - }; - char add[16] = ""; + if (state < ARRAY_SIZE(name)) { + return name[state]; + } + return "INVALID"; +} - if (state == USB_PACKET_COMPLETE) { - if (p->result < 0) { - snprintf(add, sizeof(add), " - %s", rets[-p->result]); - } else { - snprintf(add, sizeof(add), " - %d", p->result); - } +void usb_packet_check_state(USBPacket *p, USBPacketState expected) +{ + USBDevice *dev; + USBBus *bus; + + if (p->state == expected) { + return; } - fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n", - p->ep->dev->qdev.parent_bus->name, - p->ep->dev->port->path, - p->ep->dev->addr, p->ep->nr, - p, name[p->state], name[state], add); -#endif + dev = p->ep->dev; + bus = usb_bus_from_device(dev); + trace_usb_packet_state_fault(bus->busnr, dev->port->path, p->ep->nr, p, + usb_packet_state_name(p->state), + usb_packet_state_name(expected)); + assert(!"usb packet state check failed"); +} + +void usb_packet_set_state(USBPacket *p, USBPacketState state) +{ + USBDevice *dev = p->ep->dev; + USBBus *bus = usb_bus_from_device(dev); + + trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p, + usb_packet_state_name(p->state), + usb_packet_state_name(state)); p->state = state; } @@ -430,6 +499,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep) p->pid = pid; p->ep = ep; p->result = 0; + p->parameter = 0; qemu_iovec_reset(&p->iov); usb_packet_set_state(p, USB_PACKET_SETUP); } @@ -482,6 +552,7 @@ void usb_ep_init(USBDevice *dev) dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; dev->ep_ctl.dev = dev; + dev->ep_ctl.pipeline = false; QTAILQ_INIT(&dev->ep_ctl.queue); for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { dev->ep_in[ep].nr = ep + 1; @@ -494,6 +565,8 @@ void usb_ep_init(USBDevice *dev) dev->ep_out[ep].ifnum = 0; dev->ep_in[ep].dev = dev; dev->ep_out[ep].dev = dev; + dev->ep_in[ep].pipeline = false; + dev->ep_out[ep].pipeline = false; QTAILQ_INIT(&dev->ep_in[ep].queue); QTAILQ_INIT(&dev->ep_out[ep].queue); } @@ -607,3 +680,9 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep) struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); return uep->max_packet_size; } + +void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + uep->pipeline = enabled; +} diff --git a/hw/usb-desc.c b/hw/usb/desc.c index 3c3ed6a802..9847a75b83 100644 --- a/hw/usb-desc.c +++ b/hw/usb/desc.c @@ -1,5 +1,5 @@ -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" #include "trace.h" /* ------------------------------------------------------------------ */ @@ -536,7 +536,11 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = dev->config->bConfigurationValue; + /* + * 9.4.2: 0 should be returned if the device is unconfigured, otherwise + * the non zero value of bConfigurationValue. + */ + data[0] = dev->config ? dev->config->bConfigurationValue : 0; ret = 1; break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: @@ -544,9 +548,18 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, trace_usb_set_config(dev->addr, value, ret); break; - case DeviceRequest | USB_REQ_GET_STATUS: + case DeviceRequest | USB_REQ_GET_STATUS: { + const USBDescConfig *config = dev->config ? + dev->config : &dev->device->confs[0]; + data[0] = 0; - if (dev->config->bmAttributes & 0x40) { + /* + * Default state: Device behavior when this request is received while + * the device is in the Default state is not specified. + * We return the same value that a configured device would return if + * it used the first configuration. + */ + if (config->bmAttributes & 0x40) { data[0] |= 1 << USB_DEVICE_SELF_POWERED; } if (dev->remote_wakeup) { @@ -555,6 +568,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, data[1] = 0x00; ret = 2; break; + } case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (value == USB_DEVICE_REMOTE_WAKEUP) { dev->remote_wakeup = 0; diff --git a/hw/usb-desc.h b/hw/usb/desc.h index d6e07ea5d2..d6e07ea5d2 100644 --- a/hw/usb-desc.h +++ b/hw/usb/desc.h diff --git a/hw/usb-audio.c b/hw/usb/dev-audio.c index fed136117b..426b95c82b 100644 --- a/hw/usb-audio.c +++ b/hw/usb/dev-audio.c @@ -30,10 +30,10 @@ */ #include "qemu-common.h" -#include "usb.h" -#include "usb-desc.h" -#include "hw.h" -#include "audiodev.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" +#include "hw/hw.h" +#include "hw/audiodev.h" #include "audio/audio.h" #define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ diff --git a/hw/usb-bt.c b/hw/usb/dev-bluetooth.c index 649bdcf2d7..195370c24a 100644 --- a/hw/usb-bt.c +++ b/hw/usb/dev-bluetooth.c @@ -19,10 +19,10 @@ */ #include "qemu-common.h" -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" #include "net.h" -#include "bt.h" +#include "hw/bt.h" struct USBBtState { USBDevice dev; @@ -498,14 +498,14 @@ static int usb_bt_initfn(USBDevice *dev) return 0; } -USBDevice *usb_bt_init(HCIInfo *hci) +USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci) { USBDevice *dev; struct USBBtState *s; if (!hci) return NULL; - dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle"); + dev = usb_create_simple(bus, "usb-bt-dongle"); if (!dev) { return NULL; } diff --git a/hw/usb-hid.c b/hw/usb/dev-hid.c index 7fc0bd81aa..f29544d954 100644 --- a/hw/usb-hid.c +++ b/hw/usb/dev-hid.c @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "console.h" -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" #include "qemu-timer.h" -#include "hid.h" +#include "hw/hid.h" /* HID interface requests */ #define GET_REPORT 0xa101 @@ -466,6 +466,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: if (p->ep->nr == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + hid_pointer_activate(hs); + } if (!hid_has_events(hs) && (!hs->idle || hs->next_idle_clock - curtime > 0)) { return USB_RET_NAK; diff --git a/hw/usb-hub.c b/hw/usb/dev-hub.c index a12856e2e9..eb4e711207 100644 --- a/hw/usb-hub.c +++ b/hw/usb/dev-hub.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ #include "qemu-common.h" -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" //#define DEBUG diff --git a/hw/usb-net.c b/hw/usb/dev-network.c index 49d5d4db65..cff55f223e 100644 --- a/hw/usb-net.c +++ b/hw/usb/dev-network.c @@ -24,8 +24,8 @@ */ #include "qemu-common.h" -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" #include "net.h" #include "qemu-queue.h" #include "sysemu.h" @@ -1353,7 +1353,7 @@ static int usb_net_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_net_init(const char *cmdline) +static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) { USBDevice *dev; QemuOpts *opts; @@ -1371,7 +1371,7 @@ static USBDevice *usb_net_init(const char *cmdline) return NULL; } - dev = usb_create(NULL /* FIXME */, "usb-net"); + dev = usb_create(bus, "usb-net"); if (!dev) { return NULL; } diff --git a/hw/usb-serial.c b/hw/usb/dev-serial.c index 52676e8f7b..8dcac8bc88 100644 --- a/hw/usb-serial.c +++ b/hw/usb/dev-serial.c @@ -10,8 +10,8 @@ #include "qemu-common.h" #include "qemu-error.h" -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" #include "qemu-char.h" //#define DEBUG_Serial @@ -492,7 +492,7 @@ static int usb_serial_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_serial_init(const char *filename) +static USBDevice *usb_serial_init(USBBus *bus, const char *filename) { USBDevice *dev; CharDriverState *cdrv; @@ -535,7 +535,7 @@ static USBDevice *usb_serial_init(const char *filename) if (!cdrv) return NULL; - dev = usb_create(NULL /* FIXME */, "usb-serial"); + dev = usb_create(bus, "usb-serial"); if (!dev) { return NULL; } @@ -549,7 +549,7 @@ static USBDevice *usb_serial_init(const char *filename) return dev; } -static USBDevice *usb_braille_init(const char *unused) +static USBDevice *usb_braille_init(USBBus *bus, const char *unused) { USBDevice *dev; CharDriverState *cdrv; @@ -558,7 +558,7 @@ static USBDevice *usb_braille_init(const char *unused) if (!cdrv) return NULL; - dev = usb_create(NULL /* FIXME */, "usb-braille"); + dev = usb_create(bus, "usb-braille"); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); qdev_init_nofail(&dev->qdev); diff --git a/hw/usb-ccid.c b/hw/usb/dev-smartcard-reader.c index 0b2ac8037a..8e66675d86 100644 --- a/hw/usb-ccid.c +++ b/hw/usb/dev-smartcard-reader.c @@ -5,7 +5,7 @@ * * Written by Alon Levy, with contributions from Robert Relyea. * - * Based on usb-serial.c, see it's copyright and attributions below. + * Based on usb-serial.c, see its copyright and attributions below. * * This work is licensed under the terms of the GNU GPL, version 2.1 or later. * See the COPYING file in the top-level directory. @@ -36,8 +36,8 @@ #include "qemu-common.h" #include "qemu-error.h" -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" #include "monitor.h" #include "hw/ccid.h" @@ -447,7 +447,7 @@ static const USBDescDevice desc_device = { { .bNumInterfaces = 1, .bConfigurationValue = 1, - .bmAttributes = 0xa0, + .bmAttributes = 0xe0, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface0, diff --git a/hw/usb-msd.c b/hw/usb/dev-storage.c index 5fbd2d021b..bdbe7bdd11 100644 --- a/hw/usb-msd.c +++ b/hw/usb/dev-storage.c @@ -10,9 +10,9 @@ #include "qemu-common.h" #include "qemu-option.h" #include "qemu-config.h" -#include "usb.h" -#include "usb-desc.h" -#include "scsi.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" +#include "hw/scsi.h" #include "console.h" #include "monitor.h" #include "sysemu.h" @@ -193,9 +193,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) int len; DPRINTF("Command status %d tag 0x%x, len %zd\n", - s->csw.status, s->csw.tag, p->iov.size); + s->csw.status, le32_to_cpu(s->csw.tag), p->iov.size); - assert(s->csw.sig == 0x53425355); + assert(s->csw.sig == cpu_to_le32(0x53425355)); len = MIN(sizeof(s->csw), p->iov.size); usb_packet_copy(p, &s->csw, len); memset(&s->csw, 0, sizeof(s->csw)); @@ -233,7 +233,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r s->csw.sig = cpu_to_le32(0x53425355); s->csw.tag = cpu_to_le32(req->tag); - s->csw.residue = s->residue; + s->csw.residue = cpu_to_le32(s->residue); s->csw.status = status != 0; if (s->packet) { @@ -568,7 +568,7 @@ static int usb_msd_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_msd_init(const char *filename) +static USBDevice *usb_msd_init(USBBus *bus, const char *filename) { static int nr=0; char id[8]; @@ -611,7 +611,7 @@ static USBDevice *usb_msd_init(const char *filename) } /* create guest device */ - dev = usb_create(NULL /* FIXME */, "usb-storage"); + dev = usb_create(bus, "usb-storage"); if (!dev) { return NULL; } diff --git a/hw/usb-wacom.c b/hw/usb/dev-wacom.c index 197e2dced5..c1cfd74403 100644 --- a/hw/usb-wacom.c +++ b/hw/usb/dev-wacom.c @@ -25,10 +25,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "console.h" -#include "usb.h" -#include "usb-desc.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" /* Interface requests */ #define WACOM_GET_REPORT 0x2101 diff --git a/hw/usb-ehci.c b/hw/usb/hcd-ehci.c index e699814305..60f9f5bdb5 100644 --- a/hw/usb-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -22,10 +22,10 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu-timer.h" -#include "usb.h" -#include "pci.h" +#include "hw/usb.h" +#include "hw/pci.h" #include "monitor.h" #include "trace.h" #include "dma.h" @@ -347,7 +347,6 @@ enum async_state { struct EHCIQueue { EHCIState *ehci; QTAILQ_ENTRY(EHCIQueue) next; - bool async_schedule; uint32_t seen; uint64_t ts; @@ -367,6 +366,8 @@ struct EHCIQueue { int usb_status; }; +typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; + struct EHCIState { PCIDevice dev; USBBus bus; @@ -410,14 +411,14 @@ struct EHCIState { USBPort ports[NB_PORTS]; USBPort *companion_ports[NB_PORTS]; uint32_t usbsts_pending; - QTAILQ_HEAD(, EHCIQueue) queues; + EHCIQueueHead aqueues; + EHCIQueueHead pqueues; uint32_t a_fetch_addr; // which address to look at next uint32_t p_fetch_addr; // which address to look at next USBPacket ipacket; QEMUSGList isgl; - int isoch_pause; uint64_t last_run_ns; }; @@ -660,31 +661,34 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr, static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q; q = g_malloc0(sizeof(*q)); q->ehci = ehci; - q->async_schedule = async; - QTAILQ_INSERT_HEAD(&ehci->queues, q, next); + QTAILQ_INSERT_HEAD(head, q, next); trace_usb_ehci_queue_action(q, "alloc"); return q; } -static void ehci_free_queue(EHCIQueue *q) +static void ehci_free_queue(EHCIQueue *q, int async) { + EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues; trace_usb_ehci_queue_action(q, "free"); if (q->async == EHCI_ASYNC_INFLIGHT) { usb_cancel_packet(&q->packet); } - QTAILQ_REMOVE(&q->ehci->queues, q, next); + QTAILQ_REMOVE(head, q, next); g_free(q); } -static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr) +static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, + int async) { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q; - QTAILQ_FOREACH(q, &ehci->queues, next) { + QTAILQ_FOREACH(q, head, next) { if (addr == q->qhaddr) { return q; } @@ -692,43 +696,46 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr) return NULL; } -static void ehci_queues_rip_unused(EHCIState *ehci) +static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q, *tmp; - QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { + QTAILQ_FOREACH_SAFE(q, head, next, tmp) { if (q->seen) { q->seen = 0; q->ts = ehci->last_run_ns; continue; } - if (ehci->last_run_ns < q->ts + 250000000) { + if (!flush && ehci->last_run_ns < q->ts + 250000000) { /* allow 0.25 sec idle */ continue; } - ehci_free_queue(q); + ehci_free_queue(q, async); } } -static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev) +static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q, *tmp; - QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { + QTAILQ_FOREACH_SAFE(q, head, next, tmp) { if (!usb_packet_is_inflight(&q->packet) || q->packet.ep->dev != dev) { continue; } - ehci_free_queue(q); + ehci_free_queue(q, async); } } -static void ehci_queues_rip_all(EHCIState *ehci) +static void ehci_queues_rip_all(EHCIState *ehci, int async) { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q, *tmp; - QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { - ehci_free_queue(q); + QTAILQ_FOREACH_SAFE(q, head, next, tmp) { + ehci_free_queue(q, async); } } @@ -773,7 +780,8 @@ static void ehci_detach(USBPort *port) return; } - ehci_queues_rip_device(s, port->dev); + ehci_queues_rip_device(s, port->dev, 0); + ehci_queues_rip_device(s, port->dev, 1); *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); *portsc |= PORTSC_CSC; @@ -793,7 +801,8 @@ static void ehci_child_detach(USBPort *port, USBDevice *child) return; } - ehci_queues_rip_device(s, child); + ehci_queues_rip_device(s, child, 0); + ehci_queues_rip_device(s, child, 1); } static void ehci_wakeup(USBPort *port) @@ -897,7 +906,6 @@ static void ehci_reset(void *opaque) s->astate = EST_INACTIVE; s->pstate = EST_INACTIVE; - s->isoch_pause = -1; s->attach_poll_counter = 0; for(i = 0; i < NB_PORTS; i++) { @@ -911,7 +919,9 @@ static void ehci_reset(void *opaque) usb_device_reset(devs[i]); } } - ehci_queues_rip_all(s); + ehci_queues_rip_all(s, 0); + ehci_queues_rip_all(s, 1); + qemu_del_timer(s->frame_timer); } static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) @@ -1064,13 +1074,14 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) { qemu_del_timer(s->frame_timer); - // TODO - should finish out some stuff before setting halt + ehci_queues_rip_all(s, 0); + ehci_queues_rip_all(s, 1); ehci_set_usbsts(s, USBSTS_HALT); } if (val & USBCMD_HCRESET) { ehci_reset(s); - val &= ~USBCMD_HCRESET; + val = s->usbcmd; } /* not supporting dynamic frame list size at the moment */ @@ -1278,8 +1289,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) static void ehci_execute_complete(EHCIQueue *q) { - int c_err, reload; - assert(q->async != EHCI_ASYNC_INFLIGHT); q->async = EHCI_ASYNC_NONE; @@ -1287,15 +1296,11 @@ static void ehci_execute_complete(EHCIQueue *q) q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); if (q->usb_status < 0) { -err: - /* TO-DO: put this is in a function that can be invoked below as well */ - c_err = get_field(q->qh.token, QTD_TOKEN_CERR); - c_err--; - set_field(&q->qh.token, c_err, QTD_TOKEN_CERR); - switch(q->usb_status) { + case USB_RET_IOERROR: case USB_RET_NODEV: q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); + set_field(&q->qh.token, 0, QTD_TOKEN_CERR); ehci_record_interrupt(q->ehci, USBSTS_ERRINT); break; case USB_RET_STALL: @@ -1303,16 +1308,8 @@ err: ehci_record_interrupt(q->ehci, USBSTS_ERRINT); break; case USB_RET_NAK: - /* 4.10.3 */ - reload = get_field(q->qh.epchar, QH_EPCHAR_RL); - if ((q->pid == USB_TOKEN_IN) && reload) { - int nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); - nakcnt--; - set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); - } else if (!reload) { - return; - } - break; + set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT); + return; /* We're not done yet with this transaction */ case USB_RET_BABBLE: q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); ehci_record_interrupt(q->ehci, USBSTS_ERRINT); @@ -1323,15 +1320,13 @@ err: assert(0); break; } + } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) { + q->usb_status = USB_RET_BABBLE; + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); + ehci_record_interrupt(q->ehci, USBSTS_ERRINT); } else { - // DPRINTF("Short packet condition\n"); // TODO check 4.12 for splits - if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) { - q->usb_status = USB_RET_BABBLE; - goto err; - } - if (q->tbytes && q->pid == USB_TOKEN_IN) { q->tbytes -= q->usb_status; } else { @@ -1347,7 +1342,7 @@ err: q->qh.token ^= QTD_TOKEN_DTOGGLE; q->qh.token &= ~QTD_TOKEN_ACTIVE; - if ((q->usb_status >= 0) && (q->qh.token & QTD_TOKEN_IOC)) { + if (q->qh.token & QTD_TOKEN_IOC) { ehci_record_interrupt(q->ehci, USBSTS_INT); } } @@ -1458,45 +1453,41 @@ static int ehci_process_itd(EHCIState *ehci, dev = ehci_find_device(ehci, devaddr); ep = usb_ep_get(dev, pid, endp); - usb_packet_setup(&ehci->ipacket, pid, ep); - usb_packet_map(&ehci->ipacket, &ehci->isgl); - - ret = usb_handle_packet(dev, &ehci->ipacket); - - usb_packet_unmap(&ehci->ipacket); + if (ep->type == USB_ENDPOINT_XFER_ISOC) { + usb_packet_setup(&ehci->ipacket, pid, ep); + usb_packet_map(&ehci->ipacket, &ehci->isgl); + ret = usb_handle_packet(dev, &ehci->ipacket); + assert(ret != USB_RET_ASYNC); + usb_packet_unmap(&ehci->ipacket); + } else { + DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); + ret = USB_RET_NAK; + } qemu_sglist_destroy(&ehci->isgl); -#if 0 - /* In isoch, there is no facility to indicate a NAK so let's - * instead just complete a zero-byte transaction. Setting - * DBERR seems too draconian. - */ - - if (ret == USB_RET_NAK) { - if (ehci->isoch_pause > 0) { - DPRINTF("ISOCH: received a NAK but paused so returning\n"); - ehci->isoch_pause--; - return 0; - } else if (ehci->isoch_pause == -1) { - DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n"); - // Pause frindex for up to 50 msec waiting for data from - // remote - ehci->isoch_pause = 50; - return 0; - } else { - DPRINTF("ISOCH: isoch pause timeout! return 0\n"); + if (ret < 0) { + switch (ret) { + default: + fprintf(stderr, "Unexpected iso usb result: %d\n", ret); + /* Fall through */ + case USB_RET_IOERROR: + case USB_RET_NODEV: + /* 3.3.2: XACTERR is only allowed on IN transactions */ + if (dir) { + itd->transact[i] |= ITD_XACT_XACTERR; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + } + break; + case USB_RET_BABBLE: + itd->transact[i] |= ITD_XACT_BABBLE; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + break; + case USB_RET_NAK: + /* no data for us, so do a zero-length transfer */ ret = 0; + break; } - } else { - DPRINTF("ISOCH: received ACK, clearing pause\n"); - ehci->isoch_pause = -1; } -#else - if (ret == USB_RET_NAK) { - ret = 0; - } -#endif - if (ret >= 0) { if (!dir) { /* OUT */ @@ -1505,10 +1496,9 @@ static int ehci_process_itd(EHCIState *ehci, /* IN */ set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); } - - if (itd->transact[i] & ITD_XACT_IOC) { - ehci_record_interrupt(ehci, USBSTS_INT); - } + } + if (itd->transact[i] & ITD_XACT_IOC) { + ehci_record_interrupt(ehci, USBSTS_INT); } itd->transact[i] &= ~ITD_XACT_ACTIVE; } @@ -1531,7 +1521,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) ehci_set_usbsts(ehci, USBSTS_REC); } - ehci_queues_rip_unused(ehci); + ehci_queues_rip_unused(ehci, async, 0); /* Find the head of the list (4.9.1.1) */ for(i = 0; i < MAX_QH; i++) { @@ -1573,8 +1563,7 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async) int again = 0; uint32_t entry = ehci_get_fetch_addr(ehci, async); - if (entry < 0x1000) { - DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry); + if (NLPTR_TBIT(entry)) { ehci_set_state(ehci, async, EST_ACTIVE); goto out; } @@ -1616,10 +1605,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { uint32_t entry; EHCIQueue *q; - int reload; entry = ehci_get_fetch_addr(ehci, async); - q = ehci_find_queue_by_qh(ehci, entry); + q = ehci_find_queue_by_qh(ehci, entry, async); if (NULL == q) { q = ehci_alloc_queue(ehci, async); } @@ -1674,15 +1662,11 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } #endif - reload = get_field(q->qh.epchar, QH_EPCHAR_RL); - if (reload) { - set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); - } - if (q->qh.token & QTD_TOKEN_HALT) { ehci_set_state(ehci, async, EST_HORIZONTALQH); - } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && (q->qh.current_qtd > 0x1000)) { + } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && + (NLPTR_TBIT(q->qh.current_qtd) == 0)) { q->qtdaddr = q->qh.current_qtd; ehci_set_state(ehci, async, EST_FETCHQTD); @@ -1761,7 +1745,6 @@ static int ehci_state_advqueue(EHCIQueue *q, int async) * want data and alt-next qTD is valid */ if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && - (q->qh.altnext_qtd > 0x1000) && (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { q->qtdaddr = q->qh.altnext_qtd; ehci_set_state(q->ehci, async, EST_FETCHQTD); @@ -1769,8 +1752,7 @@ static int ehci_state_advqueue(EHCIQueue *q, int async) /* * next qTD is valid */ - } else if ((q->qh.next_qtd > 0x1000) && - (NLPTR_TBIT(q->qh.next_qtd) == 0)) { + } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) { q->qtdaddr = q->qh.next_qtd; ehci_set_state(q->ehci, async, EST_FETCHQTD); @@ -1839,25 +1821,11 @@ static void ehci_flush_qh(EHCIQueue *q) static int ehci_state_execute(EHCIQueue *q, int async) { int again = 0; - int reload, nakcnt; - int smask; if (ehci_qh_do_overlay(q) != 0) { return -1; } - smask = get_field(q->qh.epcap, QH_EPCAP_SMASK); - - if (!smask) { - reload = get_field(q->qh.epchar, QH_EPCHAR_RL); - nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); - if (reload && !nakcnt) { - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); - again = 1; - goto out; - } - } - // TODO verify enough time remains in the uframe as in 4.4.1.1 // TODO write back ptr to async list when done or out of time // TODO Windows does not seem to ever set the MULT field @@ -1899,7 +1867,6 @@ out: static int ehci_state_executing(EHCIQueue *q, int async) { int again = 0; - int reload, nakcnt; ehci_execute_complete(q); if (q->usb_status == USB_RET_ASYNC) { @@ -1919,21 +1886,8 @@ static int ehci_state_executing(EHCIQueue *q, int async) // counter decrements to 0 } - reload = get_field(q->qh.epchar, QH_EPCHAR_RL); - if (reload) { - nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); - if (q->usb_status == USB_RET_NAK) { - if (nakcnt) { - nakcnt--; - } - } else { - nakcnt = reload; - } - set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); - } - /* 4.10.5 */ - if ((q->usb_status == USB_RET_NAK) || (q->qh.token & QTD_TOKEN_ACTIVE)) { + if (q->usb_status == USB_RET_NAK) { ehci_set_state(q->ehci, async, EST_HORIZONTALQH); } else { ehci_set_state(q->ehci, async, EST_WRITEBACK); @@ -2071,7 +2025,7 @@ static void ehci_advance_state(EHCIState *ehci, static void ehci_advance_async_state(EHCIState *ehci) { - int async = 1; + const int async = 1; switch(ehci_get_state(ehci, async)) { case EST_INACTIVE: @@ -2084,23 +2038,13 @@ static void ehci_advance_async_state(EHCIState *ehci) case EST_ACTIVE: if ( !(ehci->usbcmd & USBCMD_ASE)) { + ehci_queues_rip_all(ehci, async); ehci_clear_usbsts(ehci, USBSTS_ASS); ehci_set_state(ehci, async, EST_INACTIVE); break; } - /* If the doorbell is set, the guest wants to make a change to the - * schedule. The host controller needs to release cached data. - * (section 4.8.2) - */ - if (ehci->usbcmd & USBCMD_IAAD) { - DPRINTF("ASYNC: doorbell request acknowledged\n"); - ehci->usbcmd &= ~USBCMD_IAAD; - ehci_set_interrupt(ehci, USBSTS_IAA); - break; - } - - /* make sure guest has acknowledged */ + /* make sure guest has acknowledged the doorbell interrupt */ /* TO-DO: is this really needed? */ if (ehci->usbsts & USBSTS_IAA) { DPRINTF("IAA status bit still set.\n"); @@ -2114,6 +2058,18 @@ static void ehci_advance_async_state(EHCIState *ehci) ehci_set_state(ehci, async, EST_WAITLISTHEAD); ehci_advance_state(ehci, async); + + /* If the doorbell is set, the guest wants to make a change to the + * schedule. The host controller needs to release cached data. + * (section 4.8.2) + */ + if (ehci->usbcmd & USBCMD_IAAD) { + /* Remove all unseen qhs from the async qhs queue */ + ehci_queues_rip_unused(ehci, async, 1); + DPRINTF("ASYNC: doorbell request acknowledged\n"); + ehci->usbcmd &= ~USBCMD_IAAD; + ehci_set_interrupt(ehci, USBSTS_IAA); + } break; default: @@ -2128,7 +2084,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) { uint32_t entry; uint32_t list; - int async = 0; + const int async = 0; // 4.6 @@ -2143,6 +2099,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) case EST_ACTIVE: if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { + ehci_queues_rip_all(ehci, async); ehci_clear_usbsts(ehci, USBSTS_PSS); ehci_set_state(ehci, async, EST_INACTIVE); break; @@ -2163,6 +2120,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) ehci_set_fetch_addr(ehci, async,entry); ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_advance_state(ehci, async); + ehci_queues_rip_unused(ehci, async, 0); break; default: @@ -2190,9 +2148,7 @@ static void ehci_frame_timer(void *opaque) for (i = 0; i < frames; i++) { if ( !(ehci->usbsts & USBSTS_HALT)) { - if (ehci->isoch_pause <= 0) { - ehci->frindex += 8; - } + ehci->frindex += 8; if (ehci->frindex > 0x00001fff) { ehci->frindex = 0; @@ -2361,15 +2317,14 @@ static int usb_ehci_initfn(PCIDevice *dev) } s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s); - QTAILQ_INIT(&s->queues); + QTAILQ_INIT(&s->aqueues); + QTAILQ_INIT(&s->pqueues); qemu_register_reset(ehci_reset, s); memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); - fprintf(stderr, "*** EHCI support is under development ***\n"); - return 0; } diff --git a/hw/usb-musb.c b/hw/usb/hcd-musb.c index 820907a9a9..fa9385ee49 100644 --- a/hw/usb-musb.c +++ b/hw/usb/hcd-musb.c @@ -22,9 +22,9 @@ */ #include "qemu-common.h" #include "qemu-timer.h" -#include "usb.h" -#include "irq.h" -#include "hw.h" +#include "hw/usb.h" +#include "hw/irq.h" +#include "hw/hw.h" /* Common USB registers */ #define MUSB_HDRC_FADDR 0x00 /* 8-bit */ diff --git a/hw/usb-ohci.c b/hw/usb/hcd-ohci.c index 7aa19fe781..1a1cc88b1f 100644 --- a/hw/usb-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -26,13 +26,12 @@ * o BIOS work to boot from USB storage */ -#include "hw.h" +#include "hw/hw.h" #include "qemu-timer.h" -#include "usb.h" -#include "pci.h" -#include "usb-ohci.h" -#include "sysbus.h" -#include "qdev-addr.h" +#include "hw/usb.h" +#include "hw/pci.h" +#include "hw/sysbus.h" +#include "hw/qdev-addr.h" //#define DEBUG_OHCI /* Dump packet contents. */ @@ -122,6 +121,11 @@ struct ohci_hcca { uint16_t frame, pad; uint32_t done; }; +#define HCCA_WRITEBACK_OFFSET offsetof(struct ohci_hcca, frame) +#define HCCA_WRITEBACK_SIZE 8 /* frame, pad, done */ + +#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) +#define ED_WBACK_SIZE 4 static void ohci_bus_stop(OHCIState *ohci); static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); @@ -569,7 +573,13 @@ static inline int ohci_read_hcca(OHCIState *ohci, static inline int ohci_put_ed(OHCIState *ohci, uint32_t addr, struct ohci_ed *ed) { - return put_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2); + /* ed->tail is under control of the HCD. + * Since just ed->head is changed by HC, just write back this + */ + + return put_dwords(ohci, addr + ED_WBACK_OFFSET, + (uint32_t *)((char *)ed + ED_WBACK_OFFSET), + ED_WBACK_SIZE >> 2); } static inline int ohci_put_td(OHCIState *ohci, @@ -588,7 +598,9 @@ static inline int ohci_put_iso_td(OHCIState *ohci, static inline int ohci_put_hcca(OHCIState *ohci, uint32_t addr, struct ohci_hcca *hcca) { - cpu_physical_memory_write(addr + ohci->localmem_base, hcca, sizeof(*hcca)); + cpu_physical_memory_write(addr + ohci->localmem_base + HCCA_WRITEBACK_OFFSET, + (char *)hcca + HCCA_WRITEBACK_OFFSET, + HCCA_WRITEBACK_SIZE); return 1; } @@ -837,6 +849,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, OHCI_CC_DATAUNDERRUN); } else { switch (ret) { + case USB_RET_IOERROR: case USB_RET_NODEV: OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, OHCI_CC_DEVICENOTRESPONDING); @@ -1052,6 +1065,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN); } else { switch (ret) { + case USB_RET_IOERROR: case USB_RET_NODEV: OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); case USB_RET_NAK: @@ -1813,11 +1827,6 @@ static int usb_ohci_initfn_pci(struct PCIDevice *dev) return 0; } -void usb_ohci_init_pci(struct PCIBus *bus, int devfn) -{ - pci_create_simple(bus, devfn, "pci-ohci"); -} - typedef struct { SysBusDevice busdev; OHCIState ohci; diff --git a/hw/usb-uhci.c b/hw/usb/hcd-uhci.c index 2280dc792d..e55dad914c 100644 --- a/hw/usb-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -25,13 +25,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "usb.h" -#include "pci.h" +#include "hw/hw.h" +#include "hw/usb.h" +#include "hw/pci.h" #include "qemu-timer.h" -#include "usb-uhci.h" #include "iov.h" #include "dma.h" +#include "trace.h" //#define DEBUG //#define DEBUG_DUMP_DATA @@ -77,41 +77,41 @@ #define NB_PORTS 2 -#ifdef DEBUG -#define DPRINTF printf - -static const char *pid2str(int pid) -{ - switch (pid) { - case USB_TOKEN_SETUP: return "SETUP"; - case USB_TOKEN_IN: return "IN"; - case USB_TOKEN_OUT: return "OUT"; - } - return "?"; -} - -#else -#define DPRINTF(...) -#endif +enum { + TD_RESULT_STOP_FRAME = 10, + TD_RESULT_COMPLETE, + TD_RESULT_NEXT_QH, + TD_RESULT_ASYNC_START, + TD_RESULT_ASYNC_CONT, +}; typedef struct UHCIState UHCIState; +typedef struct UHCIAsync UHCIAsync; +typedef struct UHCIQueue UHCIQueue; /* * Pending async transaction. * 'packet' must be the first field because completion * handler does "(UHCIAsync *) pkt" cast. */ -typedef struct UHCIAsync { + +struct UHCIAsync { USBPacket packet; QEMUSGList sgl; - UHCIState *uhci; + UHCIQueue *queue; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; - uint32_t token; - int8_t valid; uint8_t isoc; uint8_t done; -} UHCIAsync; +}; + +struct UHCIQueue { + uint32_t token; + UHCIState *uhci; + QTAILQ_ENTRY(UHCIQueue) next; + QTAILQ_HEAD(, UHCIAsync) asyncs; + int8_t valid; +}; typedef struct UHCIPort { USBPort port; @@ -137,7 +137,7 @@ struct UHCIState { uint32_t pending_int_mask; /* Active packets */ - QTAILQ_HEAD(,UHCIAsync) async_pending; + QTAILQ_HEAD(, UHCIQueue) queues; uint8_t num_ports_vmstate; /* Properties */ @@ -157,62 +157,95 @@ typedef struct UHCI_QH { uint32_t el_link; } UHCI_QH; -static UHCIAsync *uhci_async_alloc(UHCIState *s) +static inline int32_t uhci_queue_token(UHCI_TD *td) +{ + /* covers ep, dev, pid -> identifies the endpoint */ + return td->token & 0x7ffff; +} + +static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td) +{ + uint32_t token = uhci_queue_token(td); + UHCIQueue *queue; + + QTAILQ_FOREACH(queue, &s->queues, next) { + if (queue->token == token) { + return queue; + } + } + + queue = g_new0(UHCIQueue, 1); + queue->uhci = s; + queue->token = token; + QTAILQ_INIT(&queue->asyncs); + QTAILQ_INSERT_HEAD(&s->queues, queue, next); + trace_usb_uhci_queue_add(queue->token); + return queue; +} + +static void uhci_queue_free(UHCIQueue *queue) { - UHCIAsync *async = g_malloc(sizeof(UHCIAsync)); - - memset(&async->packet, 0, sizeof(async->packet)); - async->uhci = s; - async->valid = 0; - async->td = 0; - async->token = 0; - async->done = 0; - async->isoc = 0; + UHCIState *s = queue->uhci; + + trace_usb_uhci_queue_del(queue->token); + QTAILQ_REMOVE(&s->queues, queue, next); + g_free(queue); +} + +static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t addr) +{ + UHCIAsync *async = g_new0(UHCIAsync, 1); + + async->queue = queue; + async->td = addr; usb_packet_init(&async->packet); - pci_dma_sglist_init(&async->sgl, &s->dev, 1); + pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1); + trace_usb_uhci_packet_add(async->queue->token, async->td); return async; } -static void uhci_async_free(UHCIState *s, UHCIAsync *async) +static void uhci_async_free(UHCIAsync *async) { + trace_usb_uhci_packet_del(async->queue->token, async->td); usb_packet_cleanup(&async->packet); qemu_sglist_destroy(&async->sgl); g_free(async); } -static void uhci_async_link(UHCIState *s, UHCIAsync *async) +static void uhci_async_link(UHCIAsync *async) { - QTAILQ_INSERT_HEAD(&s->async_pending, async, next); + UHCIQueue *queue = async->queue; + QTAILQ_INSERT_TAIL(&queue->asyncs, async, next); + trace_usb_uhci_packet_link_async(async->queue->token, async->td); } -static void uhci_async_unlink(UHCIState *s, UHCIAsync *async) +static void uhci_async_unlink(UHCIAsync *async) { - QTAILQ_REMOVE(&s->async_pending, async, next); + UHCIQueue *queue = async->queue; + QTAILQ_REMOVE(&queue->asyncs, async, next); + trace_usb_uhci_packet_unlink_async(async->queue->token, async->td); } -static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) +static void uhci_async_cancel(UHCIAsync *async) { - DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n", - async->td, async->token, async->done); - + trace_usb_uhci_packet_cancel(async->queue->token, async->td, async->done); if (!async->done) usb_cancel_packet(&async->packet); - uhci_async_free(s, async); + uhci_async_free(async); } /* * Mark all outstanding async packets as invalid. * This is used for canceling them when TDs are removed by the HCD. */ -static UHCIAsync *uhci_async_validate_begin(UHCIState *s) +static void uhci_async_validate_begin(UHCIState *s) { - UHCIAsync *async; + UHCIQueue *queue; - QTAILQ_FOREACH(async, &s->async_pending, next) { - async->valid--; + QTAILQ_FOREACH(queue, &s->queues, next) { + queue->valid--; } - return NULL; } /* @@ -220,77 +253,75 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) */ static void uhci_async_validate_end(UHCIState *s) { - UHCIAsync *curr, *n; + UHCIQueue *queue, *n; + UHCIAsync *async; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - if (curr->valid > 0) { + QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { + if (queue->valid > 0) { continue; } - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); + while (!QTAILQ_EMPTY(&queue->asyncs)) { + async = QTAILQ_FIRST(&queue->asyncs); + uhci_async_unlink(async); + uhci_async_cancel(async); + } + uhci_queue_free(queue); } } static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) { + UHCIQueue *queue; UHCIAsync *curr, *n; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - if (!usb_packet_is_inflight(&curr->packet) || - curr->packet.ep->dev != dev) { - continue; + QTAILQ_FOREACH(queue, &s->queues, next) { + QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) { + if (!usb_packet_is_inflight(&curr->packet) || + curr->packet.ep->dev != dev) { + continue; + } + uhci_async_unlink(curr); + uhci_async_cancel(curr); } - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); } } static void uhci_async_cancel_all(UHCIState *s) { + UHCIQueue *queue; UHCIAsync *curr, *n; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); + QTAILQ_FOREACH(queue, &s->queues, next) { + QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) { + uhci_async_unlink(curr); + uhci_async_cancel(curr); + } + uhci_queue_free(queue); } } -static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token) +static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td) { + uint32_t token = uhci_queue_token(td); + UHCIQueue *queue; UHCIAsync *async; - UHCIAsync *match = NULL; - int count = 0; - - /* - * We're looking for the best match here. ie both td addr and token. - * Otherwise we return last good match. ie just token. - * It's ok to match just token because it identifies the transaction - * rather well, token includes: device addr, endpoint, size, etc. - * - * Also since we queue async transactions in reverse order by returning - * last good match we restores the order. - * - * It's expected that we wont have a ton of outstanding transactions. - * If we ever do we'd want to optimize this algorithm. - */ - QTAILQ_FOREACH(async, &s->async_pending, next) { - if (async->token == token) { - /* Good match */ - match = async; - - if (async->td == addr) { - /* Best match */ - break; - } + QTAILQ_FOREACH(queue, &s->queues, next) { + if (queue->token == token) { + break; } - count++; + } + if (queue == NULL) { + return NULL; } - if (count > 64) - fprintf(stderr, "uhci: warning lots of async transactions\n"); + QTAILQ_FOREACH(async, &queue->asyncs, next) { + if (async->td == addr) { + return async; + } + } - return match; + return NULL; } static void uhci_update_irq(UHCIState *s) @@ -316,7 +347,7 @@ static void uhci_reset(void *opaque) int i; UHCIPort *port; - DPRINTF("uhci: full reset\n"); + trace_usb_uhci_reset(); pci_conf = s->dev.config; @@ -416,12 +447,13 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) UHCIState *s = opaque; addr &= 0x1f; - DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val); + trace_usb_uhci_mmio_writew(addr, val); switch(addr) { case 0x00: if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { /* start frame processing */ + trace_usb_uhci_schedule_start(); s->expire_time = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / FRAME_TIMER_FREQ); qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); @@ -526,7 +558,7 @@ static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr) break; } - DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val); + trace_usb_uhci_mmio_readw(addr, val); return val; } @@ -536,7 +568,7 @@ static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val) UHCIState *s = opaque; addr &= 0x1f; - DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val); + trace_usb_uhci_mmio_writel(addr, val); switch(addr) { case 0x08: @@ -559,6 +591,7 @@ static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr) val = 0xffffffff; break; } + trace_usb_uhci_mmio_readl(addr, val); return val; } @@ -694,13 +727,15 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ - DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token); - return 1; + trace_usb_uhci_packet_complete_shortxfer(async->queue->token, + async->td); + return TD_RESULT_NEXT_QH; } } /* success */ - return 0; + trace_usb_uhci_packet_complete_success(async->queue->token, async->td); + return TD_RESULT_COMPLETE; out: switch(ret) { @@ -712,7 +747,8 @@ out: *int_mask |= 0x01; } uhci_update_irq(s); - return 1; + trace_usb_uhci_packet_complete_stall(async->queue->token, async->td); + return TD_RESULT_NEXT_QH; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; @@ -723,14 +759,16 @@ out: } uhci_update_irq(s); /* frame interrupted */ - return -1; + trace_usb_uhci_packet_complete_babble(async->queue->token, async->td); + return TD_RESULT_STOP_FRAME; case USB_RET_NAK: td->ctrl |= TD_CTRL_NAK; if (pid == USB_TOKEN_SETUP) break; - return 1; + return TD_RESULT_NEXT_QH; + case USB_RET_IOERROR: case USB_RET_NODEV: default: break; @@ -748,61 +786,47 @@ out: if (td->ctrl & TD_CTRL_IOC) *int_mask |= 0x01; uhci_update_irq(s); + trace_usb_uhci_packet_complete_error(async->queue->token, + async->td); } } td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | (err << TD_CTRL_ERROR_SHIFT); - return 1; + return TD_RESULT_NEXT_QH; } static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask) { UHCIAsync *async; int len = 0, max_len; - uint8_t pid, isoc; - uint32_t token; + uint8_t pid; USBDevice *dev; USBEndpoint *ep; /* Is active ? */ if (!(td->ctrl & TD_CTRL_ACTIVE)) - return 1; + return TD_RESULT_NEXT_QH; - /* token field is not unique for isochronous requests, - * so use the destination buffer - */ - if (td->ctrl & TD_CTRL_IOS) { - token = td->buffer; - isoc = 1; - } else { - token = td->token; - isoc = 0; - } - - async = uhci_async_find_td(s, addr, token); + async = uhci_async_find_td(s, addr, td); if (async) { /* Already submitted */ - async->valid = 32; + async->queue->valid = 32; if (!async->done) - return 1; + return TD_RESULT_ASYNC_CONT; - uhci_async_unlink(s, async); + uhci_async_unlink(async); goto done; } /* Allocate new packet */ - async = uhci_async_alloc(s); - if (!async) - return 1; + async = uhci_async_alloc(uhci_queue_get(s, td), addr); /* valid needs to be large enough to handle 10 frame delay * for initial isochronous requests */ - async->valid = 32; - async->td = addr; - async->token = token; - async->isoc = isoc; + async->queue->valid = 32; + async->isoc = td->ctrl & TD_CTRL_IOS; max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; @@ -827,15 +851,15 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in default: /* invalid pid : frame interrupted */ - uhci_async_free(s, async); + uhci_async_free(async); s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); - return -1; + return TD_RESULT_STOP_FRAME; } if (len == USB_RET_ASYNC) { - uhci_async_link(s, async); - return 2; + uhci_async_link(async); + return TD_RESULT_ASYNC_START; } async->packet.result = len; @@ -843,16 +867,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in done: len = uhci_complete_td(s, td, async, int_mask); usb_packet_unmap(&async->packet); - uhci_async_free(s, async); + uhci_async_free(async); return len; } static void uhci_async_complete(USBPort *port, USBPacket *packet) { UHCIAsync *async = container_of(packet, UHCIAsync, packet); - UHCIState *s = async->uhci; - - DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); + UHCIState *s = async->queue->uhci; if (async->isoc) { UHCI_TD td; @@ -865,14 +887,14 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); - uhci_async_unlink(s, async); + uhci_async_unlink(async); uhci_complete_td(s, &td, async, &int_mask); s->pending_int_mask |= int_mask; /* update the status bits of the TD */ val = cpu_to_le32(td.ctrl); pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); - uhci_async_free(s, async); + uhci_async_free(async); } else { async->done = 1; uhci_process_frame(s); @@ -921,6 +943,34 @@ static int qhdb_insert(QhDb *db, uint32_t addr) return 0; } +static void uhci_fill_queue(UHCIState *s, UHCI_TD *td) +{ + uint32_t int_mask = 0; + uint32_t plink = td->link; + uint32_t token = uhci_queue_token(td); + UHCI_TD ptd; + int ret; + + while (is_valid(plink)) { + pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd)); + le32_to_cpus(&ptd.link); + le32_to_cpus(&ptd.ctrl); + le32_to_cpus(&ptd.token); + le32_to_cpus(&ptd.buffer); + if (!(ptd.ctrl & TD_CTRL_ACTIVE)) { + break; + } + if (uhci_queue_token(&ptd) != token) { + break; + } + trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token); + ret = uhci_handle_td(s, plink, &ptd, &int_mask); + assert(ret == TD_RESULT_ASYNC_START); + assert(int_mask == 0); + plink = ptd.link; + } +} + static void uhci_process_frame(UHCIState *s) { uint32_t frame_addr, link, old_td_ctrl, val, int_mask; @@ -932,8 +982,6 @@ static void uhci_process_frame(UHCIState *s) frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); - DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr); - pci_dma_read(&s->dev, frame_addr, &link, 4); le32_to_cpus(&link); @@ -945,6 +993,7 @@ static void uhci_process_frame(UHCIState *s) for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) { if (is_qh(link)) { /* QH */ + trace_usb_uhci_qh_load(link & ~0xf); if (qhdb_insert(&qhdb, link)) { /* @@ -957,14 +1006,14 @@ static void uhci_process_frame(UHCIState *s) * (b) we've reached the usb 1.1 bandwidth, which is * 1280 bytes/frame. */ - DPRINTF("uhci: detected loop. qh 0x%x\n", link); if (td_count == 0) { - DPRINTF("uhci: no transaction last round, stop\n"); + trace_usb_uhci_frame_loop_stop_idle(); break; } else if (bytes_count >= 1280) { - DPRINTF("uhci: bandwidth limit reached, stop\n"); + trace_usb_uhci_frame_loop_stop_bandwidth(); break; } else { + trace_usb_uhci_frame_loop_continue(); td_count = 0; qhdb_reset(&qhdb); qhdb_insert(&qhdb, link); @@ -975,9 +1024,6 @@ static void uhci_process_frame(UHCIState *s) le32_to_cpus(&qh.link); le32_to_cpus(&qh.el_link); - DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n", - link, qh.link, qh.el_link); - if (!is_valid(qh.el_link)) { /* QH w/o elements */ curr_qh = 0; @@ -996,9 +1042,7 @@ static void uhci_process_frame(UHCIState *s) le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); - - DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", - link, td.link, td.ctrl, td.token, curr_qh); + trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token); old_td_ctrl = td.ctrl; ret = uhci_handle_td(s, link, &td, &int_mask); @@ -1008,49 +1052,52 @@ static void uhci_process_frame(UHCIState *s) pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); } - if (ret < 0) { - /* interrupted frame */ - break; - } - - if (ret == 2 || ret == 1) { - DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", - link, ret == 2 ? "pend" : "skip", - td.link, td.ctrl, td.token, curr_qh); + switch (ret) { + case TD_RESULT_STOP_FRAME: /* interrupted frame */ + goto out; + case TD_RESULT_NEXT_QH: + case TD_RESULT_ASYNC_CONT: + trace_usb_uhci_td_nextqh(curr_qh & ~0xf, link & ~0xf); link = curr_qh ? qh.link : td.link; continue; - } - - /* completed TD */ - - DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", - link, td.link, td.ctrl, td.token, curr_qh); - - link = td.link; - td_count++; - bytes_count += (td.ctrl & 0x7ff) + 1; - - if (curr_qh) { - /* update QH element link */ - qh.el_link = link; - val = cpu_to_le32(qh.el_link); - pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); - if (!depth_first(link)) { - /* done with this QH */ - - DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n", - curr_qh, qh.link, qh.el_link); + case TD_RESULT_ASYNC_START: + trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf); + if (is_valid(td.link)) { + uhci_fill_queue(s, &td); + } + link = curr_qh ? qh.link : td.link; + continue; - curr_qh = 0; - link = qh.link; + case TD_RESULT_COMPLETE: + trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf); + link = td.link; + td_count++; + bytes_count += (td.ctrl & 0x7ff) + 1; + + if (curr_qh) { + /* update QH element link */ + qh.el_link = link; + val = cpu_to_le32(qh.el_link); + pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); + + if (!depth_first(link)) { + /* done with this QH */ + curr_qh = 0; + link = qh.link; + } } + break; + + default: + assert(!"unknown return code"); } /* go to the next entry */ } +out: s->pending_int_mask |= int_mask; } @@ -1063,11 +1110,11 @@ static void uhci_frame_timer(void *opaque) if (!(s->cmd & UHCI_CMD_RS)) { /* Full stop */ + trace_usb_uhci_schedule_stop(); qemu_del_timer(s->frame_timer); + uhci_async_cancel_all(s); /* set hchalted bit in status - UHCI11D 2.1.2 */ s->status |= UHCI_STS_HCHALTED; - - DPRINTF("uhci: halted\n"); return; } @@ -1082,7 +1129,7 @@ static void uhci_frame_timer(void *opaque) /* Start new frame */ s->frnum = (s->frnum + 1) & 0x7ff; - DPRINTF("uhci: new frame #%u\n" , s->frnum); + trace_usb_uhci_frame_start(s->frnum); uhci_async_validate_begin(s); @@ -1148,7 +1195,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) } s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; - QTAILQ_INIT(&s->async_pending); + QTAILQ_INIT(&s->queues); qemu_register_reset(uhci_reset, s); @@ -1329,18 +1376,3 @@ static void uhci_register_types(void) } type_init(uhci_register_types) - -void usb_uhci_piix3_init(PCIBus *bus, int devfn) -{ - pci_create_simple(bus, devfn, "piix3-usb-uhci"); -} - -void usb_uhci_piix4_init(PCIBus *bus, int devfn) -{ - pci_create_simple(bus, devfn, "piix4-usb-uhci"); -} - -void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn) -{ - pci_create_simple(bus, devfn, "vt82c686b-usb-uhci"); -} diff --git a/hw/usb-xhci.c b/hw/usb/hcd-xhci.c index 008b0b5718..73b0c7f5e5 100644 --- a/hw/usb-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -18,12 +18,12 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu-timer.h" -#include "usb.h" -#include "pci.h" -#include "qdev-addr.h" -#include "msi.h" +#include "hw/usb.h" +#include "hw/pci.h" +#include "hw/qdev-addr.h" +#include "hw/msi.h" //#define DEBUG_XHCI //#define DEBUG_DATA @@ -1470,8 +1470,8 @@ static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) { XHCITRB *trb_setup, *trb_status; - uint8_t bmRequestType, bRequest; - uint16_t wValue, wLength, wIndex; + uint8_t bmRequestType; + uint16_t wLength; XHCIPort *port; USBDevice *dev; int ret; @@ -1508,9 +1508,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) } bmRequestType = trb_setup->parameter; - bRequest = trb_setup->parameter >> 8; - wValue = trb_setup->parameter >> 16; - wIndex = trb_setup->parameter >> 32; wLength = trb_setup->parameter >> 48; if (xfer->data && xfer->data_alloced < wLength) { @@ -1537,12 +1534,12 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xfer->iso_xfer = false; xhci_setup_packet(xfer, dev); + xfer->packet.parameter = trb_setup->parameter; if (!xfer->in_xfer) { xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0); } - ret = usb_device_handle_control(dev, &xfer->packet, - (bmRequestType << 8) | bRequest, - wValue, wIndex, wLength, xfer->data); + + ret = usb_handle_packet(dev, &xfer->packet); xhci_complete_packet(xfer, ret); if (!xfer->running_async && !xfer->running_retry) { @@ -1769,12 +1766,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid epctx->retry = xfer; break; } - - /* - * Qemu usb can't handle multiple in-flight xfers. - * Stop here for now. - */ - break; } } @@ -2288,7 +2279,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) int nr = port->port.index + 1; port->portsc = PORTSC_PP; - if (port->port.dev && !is_detach) { + if (port->port.dev && port->port.dev->attached && !is_detach) { port->portsc |= PORTSC_CCS; switch (port->port.dev->speed) { case USB_SPEED_LOW: diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c new file mode 100644 index 0000000000..ec26266620 --- /dev/null +++ b/hw/usb/host-bsd.c @@ -0,0 +1,647 @@ +/* + * BSD host USB redirector + * + * Copyright (c) 2006 Lonnie Mendez + * Portions of code and concepts borrowed from + * usb-linux.c and libusb's bsd.c and are copyright their respective owners. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "monitor.h" +#include "hw/usb.h" + +/* usb.h declares these */ +#undef USB_SPEED_HIGH +#undef USB_SPEED_FULL +#undef USB_SPEED_LOW + +#include <sys/ioctl.h> +#ifndef __DragonFly__ +#include <dev/usb/usb.h> +#else +#include <bus/usb/usb.h> +#endif + +/* This value has maximum potential at 16. + * You should also set hw.usb.debug to gain + * more detailed view. + */ +//#define DEBUG +#define UGEN_DEBUG_LEVEL 0 + + +typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id, + int vendor_id, int product_id, + const char *product_name, int speed); +static int usb_host_find_device(int *pbus_num, int *paddr, + const char *devname); + +typedef struct USBHostDevice { + USBDevice dev; + int ep_fd[USB_MAX_ENDPOINTS]; + int devfd; + char devpath[32]; +} USBHostDevice; + + +static int ensure_ep_open(USBHostDevice *dev, int ep, int mode) +{ + char buf[32]; + int fd; + + /* Get the address for this endpoint */ + ep = UE_GET_ADDR(ep); + + if (dev->ep_fd[ep] < 0) { +#if defined(__FreeBSD__) || defined(__DragonFly__) + snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep); +#else + snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep); +#endif + /* Try to open it O_RDWR first for those devices which have in and out + * endpoints with the same address (eg 0x02 and 0x82) + */ + fd = open(buf, O_RDWR); + if (fd < 0 && errno == ENXIO) + fd = open(buf, mode); + if (fd < 0) { +#ifdef DEBUG + printf("ensure_ep_open: failed to open device endpoint %s: %s\n", + buf, strerror(errno)); +#endif + } + dev->ep_fd[ep] = fd; + } + + return dev->ep_fd[ep]; +} + +static void ensure_eps_closed(USBHostDevice *dev) +{ + int epnum = 1; + + if (!dev) + return; + + while (epnum < USB_MAX_ENDPOINTS) { + if (dev->ep_fd[epnum] >= 0) { + close(dev->ep_fd[epnum]); + dev->ep_fd[epnum] = -1; + } + epnum++; + } +} + +static void usb_host_handle_reset(USBDevice *dev) +{ +#if 0 + USBHostDevice *s = (USBHostDevice *)dev; +#endif +} + +/* XXX: + * -check device states against transfer requests + * and return appropriate response + */ +static int usb_host_handle_control(USBDevice *dev, + USBPacket *p, + int request, + int value, + int index, + int length, + uint8_t *data) +{ + USBHostDevice *s = (USBHostDevice *)dev; + struct usb_ctl_request req; + struct usb_alt_interface aiface; + int ret, timeout = 50; + + if ((request >> 8) == UT_WRITE_DEVICE && + (request & 0xff) == UR_SET_ADDRESS) { + + /* specific SET_ADDRESS support */ + dev->addr = value; + return 0; + } else if ((request >> 8) == UT_WRITE_DEVICE && + (request & 0xff) == UR_SET_CONFIG) { + + ensure_eps_closed(s); /* can't do this without all eps closed */ + + ret = ioctl(s->devfd, USB_SET_CONFIG, &value); + if (ret < 0) { +#ifdef DEBUG + printf("handle_control: failed to set configuration - %s\n", + strerror(errno)); +#endif + return USB_RET_STALL; + } + + return 0; + } else if ((request >> 8) == UT_WRITE_INTERFACE && + (request & 0xff) == UR_SET_INTERFACE) { + + aiface.uai_interface_index = index; + aiface.uai_alt_no = value; + + ensure_eps_closed(s); /* can't do this without all eps closed */ + ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface); + if (ret < 0) { +#ifdef DEBUG + printf("handle_control: failed to set alternate interface - %s\n", + strerror(errno)); +#endif + return USB_RET_STALL; + } + + return 0; + } else { + req.ucr_request.bmRequestType = request >> 8; + req.ucr_request.bRequest = request & 0xff; + USETW(req.ucr_request.wValue, value); + USETW(req.ucr_request.wIndex, index); + USETW(req.ucr_request.wLength, length); + req.ucr_data = data; + req.ucr_flags = USBD_SHORT_XFER_OK; + + ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout); +#if defined(__NetBSD__) || defined(__OpenBSD__) + if (ret < 0 && errno != EINVAL) { +#else + if (ret < 0) { +#endif +#ifdef DEBUG + printf("handle_control: setting timeout failed - %s\n", + strerror(errno)); +#endif + } + + ret = ioctl(s->devfd, USB_DO_REQUEST, &req); + /* ugen returns EIO for usbd_do_request_ no matter what + * happens with the transfer */ + if (ret < 0) { +#ifdef DEBUG + printf("handle_control: error after request - %s\n", + strerror(errno)); +#endif + return USB_RET_NAK; // STALL + } else { + return req.ucr_actlen; + } + } +} + +static int usb_host_handle_data(USBDevice *dev, USBPacket *p) +{ + USBHostDevice *s = (USBHostDevice *)dev; + int ret, fd, mode; + int one = 1, shortpacket = 0, timeout = 50; + sigset_t new_mask, old_mask; + uint8_t devep = p->ep->nr; + + /* protect data transfers from SIGALRM signal */ + sigemptyset(&new_mask); + sigaddset(&new_mask, SIGALRM); + sigprocmask(SIG_BLOCK, &new_mask, &old_mask); + + if (p->pid == USB_TOKEN_IN) { + devep |= 0x80; + mode = O_RDONLY; + shortpacket = 1; + } else { + mode = O_WRONLY; + } + + fd = ensure_ep_open(s, devep, mode); + if (fd < 0) { + sigprocmask(SIG_SETMASK, &old_mask, NULL); + return USB_RET_NODEV; + } + + if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) { +#ifdef DEBUG + printf("handle_data: failed to set timeout - %s\n", + strerror(errno)); +#endif + } + + if (shortpacket) { + if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) { +#ifdef DEBUG + printf("handle_data: failed to set short xfer mode - %s\n", + strerror(errno)); +#endif + sigprocmask(SIG_SETMASK, &old_mask, NULL); + } + } + + if (p->pid == USB_TOKEN_IN) + ret = readv(fd, p->iov.iov, p->iov.niov); + else + ret = writev(fd, p->iov.iov, p->iov.niov); + + sigprocmask(SIG_SETMASK, &old_mask, NULL); + + if (ret < 0) { +#ifdef DEBUG + printf("handle_data: error after %s data - %s\n", + pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno)); +#endif + switch(errno) { + case ETIMEDOUT: + case EINTR: + return USB_RET_NAK; + default: + return USB_RET_STALL; + } + } else { + return ret; + } +} + +static void usb_host_handle_destroy(USBDevice *opaque) +{ + USBHostDevice *s = (USBHostDevice *)opaque; + int i; + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + if (s->ep_fd[i] >= 0) + close(s->ep_fd[i]); + + if (s->devfd < 0) + return; + + close(s->devfd); + + g_free(s); +} + +static int usb_host_initfn(USBDevice *dev) +{ + return 0; +} + +USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname) +{ + struct usb_device_info bus_info, dev_info; + USBDevice *d = NULL, *ret = NULL; + USBHostDevice *dev; + char ctlpath[PATH_MAX + 1]; + char buspath[PATH_MAX + 1]; + int bfd, dfd, bus, address, i; + int ugendebug = UGEN_DEBUG_LEVEL; + + if (usb_host_find_device(&bus, &address, devname) < 0) { + goto fail; + } + + snprintf(buspath, PATH_MAX, "/dev/usb%d", bus); + + bfd = open(buspath, O_RDWR); + if (bfd < 0) { +#ifdef DEBUG + printf("usb_host_device_open: failed to open usb bus - %s\n", + strerror(errno)); +#endif + goto fail; + } + + bus_info.udi_addr = address; + if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) { +#ifdef DEBUG + printf("usb_host_device_open: failed to grab bus information - %s\n", + strerror(errno)); +#endif + goto fail_bfd; + } + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]); +#else + snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]); +#endif + + dfd = open(ctlpath, O_RDWR); + if (dfd < 0) { + dfd = open(ctlpath, O_RDONLY); + if (dfd < 0) { +#ifdef DEBUG + printf("usb_host_device_open: failed to open usb device %s - %s\n", + ctlpath, strerror(errno)); +#endif + } + goto fail_dfd; + } + + if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) { +#ifdef DEBUG + printf("usb_host_device_open: failed to grab device info - %s\n", + strerror(errno)); +#endif + goto fail_dfd; + } + + d = usb_create(guest_bus, "usb-host"); + dev = DO_UPCAST(USBHostDevice, dev, d); + + if (dev_info.udi_speed == 1) { + dev->dev.speed = USB_SPEED_LOW - 1; + dev->dev.speedmask = USB_SPEED_MASK_LOW; + } else { + dev->dev.speed = USB_SPEED_FULL - 1; + dev->dev.speedmask = USB_SPEED_MASK_FULL; + } + + if (strncmp(dev_info.udi_product, "product", 7) != 0) { + pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), + dev_info.udi_product); + } else { + snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), + "host:%s", devname); + } + + pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/"); + pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]); + + /* Mark the endpoints as not yet open */ + for (i = 0; i < USB_MAX_ENDPOINTS; i++) { + dev->ep_fd[i] = -1; + } + + ioctl(dfd, USB_SETDEBUG, &ugendebug); + + ret = (USBDevice *)dev; + +fail_dfd: + close(dfd); +fail_bfd: + close(bfd); +fail: + return ret; +} + +static void usb_host_class_initfn(ObjectClass *klass, void *data) +{ + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->product_desc = "USB Host Device"; + uc->init = usb_host_initfn; + uc->handle_reset = usb_host_handle_reset; + uc->handle_control = usb_host_handle_control; + uc->handle_data = usb_host_handle_data; + uc->handle_destroy = usb_host_handle_destroy; +} + +static TypeInfo usb_host_dev_info = { + .name = "usb-host", + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(USBHostDevice), + .class_init = usb_host_class_initfn, +}; + +static void usb_host_register_types(void) +{ + type_register_static(&usb_host_dev_info); +} + +type_init(usb_host_register_types) + +static int usb_host_scan(void *opaque, USBScanFunc *func) +{ + struct usb_device_info bus_info; + struct usb_device_info dev_info; + uint16_t vendor_id, product_id, class_id, speed; + int bfd, dfd, bus, address; + char busbuf[20], devbuf[20], product_name[256]; + int ret = 0; + + for (bus = 0; bus < 10; bus++) { + + snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus); + bfd = open(busbuf, O_RDWR); + if (bfd < 0) + continue; + + for (address = 1; address < 127; address++) { + + bus_info.udi_addr = address; + if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) + continue; + + /* only list devices that can be used by generic layer */ + if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0) + continue; + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]); +#else + snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]); +#endif + + dfd = open(devbuf, O_RDONLY); + if (dfd < 0) { +#ifdef DEBUG + printf("usb_host_scan: couldn't open device %s - %s\n", devbuf, + strerror(errno)); +#endif + continue; + } + + if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) + printf("usb_host_scan: couldn't get device information for %s - %s\n", + devbuf, strerror(errno)); + + /* XXX: might need to fixup endianness of word values before copying over */ + + vendor_id = dev_info.udi_vendorNo; + product_id = dev_info.udi_productNo; + class_id = dev_info.udi_class; + speed = dev_info.udi_speed; + + if (strncmp(dev_info.udi_product, "product", 7) != 0) + pstrcpy(product_name, sizeof(product_name), + dev_info.udi_product); + else + product_name[0] = '\0'; + + ret = func(opaque, bus, address, class_id, vendor_id, + product_id, product_name, speed); + + close(dfd); + + if (ret) + goto the_end; + } + + close(bfd); + } + +the_end: + return ret; +} + +typedef struct FindDeviceState { + int vendor_id; + int product_id; + int bus_num; + int addr; +} FindDeviceState; + +static int usb_host_find_device_scan(void *opaque, int bus_num, int addr, + int class_id, + int vendor_id, int product_id, + const char *product_name, int speed) +{ + FindDeviceState *s = opaque; + if (vendor_id == s->vendor_id && + product_id == s->product_id) { + s->bus_num = bus_num; + s->addr = addr; + return 1; + } else { + return 0; + } +} + + +/* the syntax is : + 'bus.addr' (decimal numbers) or + 'vendor_id:product_id' (hexa numbers) */ +static int usb_host_find_device(int *pbus_num, int *paddr, + const char *devname) +{ + const char *p; + int ret; + FindDeviceState fs; + + p = strchr(devname, '.'); + if (p) { + *pbus_num = strtoul(devname, NULL, 0); + *paddr = strtoul(p + 1, NULL, 0); + return 0; + } + p = strchr(devname, ':'); + if (p) { + fs.vendor_id = strtoul(devname, NULL, 16); + fs.product_id = strtoul(p + 1, NULL, 16); + ret = usb_host_scan(&fs, usb_host_find_device_scan); + if (ret) { + *pbus_num = fs.bus_num; + *paddr = fs.addr; + return 0; + } + } + return -1; +} + +/**********************/ +/* USB host device info */ + +struct usb_class_info { + int class; + const char *class_name; +}; + +static const struct usb_class_info usb_class_info[] = { + { USB_CLASS_AUDIO, "Audio"}, + { USB_CLASS_COMM, "Communication"}, + { USB_CLASS_HID, "HID"}, + { USB_CLASS_HUB, "Hub" }, + { USB_CLASS_PHYSICAL, "Physical" }, + { USB_CLASS_PRINTER, "Printer" }, + { USB_CLASS_MASS_STORAGE, "Storage" }, + { USB_CLASS_CDC_DATA, "Data" }, + { USB_CLASS_APP_SPEC, "Application Specific" }, + { USB_CLASS_VENDOR_SPEC, "Vendor Specific" }, + { USB_CLASS_STILL_IMAGE, "Still Image" }, + { USB_CLASS_CSCID, "Smart Card" }, + { USB_CLASS_CONTENT_SEC, "Content Security" }, + { -1, NULL } +}; + +static const char *usb_class_str(uint8_t class) +{ + const struct usb_class_info *p; + for (p = usb_class_info; p->class != -1; p++) { + if (p->class == class) + break; + } + return p->class_name; +} + +static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) +{ + const char *class_str, *speed_str; + + switch(speed) { + case USB_SPEED_LOW: + speed_str = "1.5"; + break; + case USB_SPEED_FULL: + speed_str = "12"; + break; + case USB_SPEED_HIGH: + speed_str = "480"; + break; + default: + speed_str = "?"; + break; + } + + monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n", + bus_num, addr, speed_str); + class_str = usb_class_str(class_id); + if (class_str) + monitor_printf(mon, " %s:", class_str); + else + monitor_printf(mon, " Class %02x:", class_id); + monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id); + if (product_name[0] != '\0') + monitor_printf(mon, ", %s", product_name); + monitor_printf(mon, "\n"); +} + +static int usb_host_info_device(void *opaque, + int bus_num, int addr, + int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) +{ + Monitor *mon = opaque; + + usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id, + product_name, speed); + return 0; +} + +void usb_host_info(Monitor *mon) +{ + usb_host_scan(mon, usb_host_info_device); +} + +/* XXX add this */ +int usb_host_device_close(const char *devname) +{ + return 0; +} diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c new file mode 100644 index 0000000000..90919c242a --- /dev/null +++ b/hw/usb/host-linux.c @@ -0,0 +1,1913 @@ +/* + * Linux host USB redirector + * + * Copyright (c) 2005 Fabrice Bellard + * + * Copyright (c) 2008 Max Krasnyansky + * Support for host device auto connect & disconnect + * Major rewrite to support fully async operation + * + * Copyright 2008 TJ <linux@tjworld.net> + * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition + * to the legacy /proc/bus/usb USB device discovery and handling + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu-timer.h" +#include "monitor.h" +#include "sysemu.h" +#include "trace.h" + +#include <dirent.h> +#include <sys/ioctl.h> + +#include <linux/usbdevice_fs.h> +#include <linux/version.h> +#include "hw/usb.h" + +/* We redefine it to avoid version problems */ +struct usb_ctrltransfer { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + uint32_t timeout; + void *data; +}; + +typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port, + int class_id, int vendor_id, int product_id, + const char *product_name, int speed); + +//#define DEBUG + +#ifdef DEBUG +#define DPRINTF printf +#else +#define DPRINTF(...) +#endif + +#define PRODUCT_NAME_SZ 32 +#define MAX_PORTLEN 16 + +/* endpoint association data */ +#define ISO_FRAME_DESC_PER_URB 32 + +/* devio.c limits single requests to 16k */ +#define MAX_USBFS_BUFFER_SIZE 16384 + +typedef struct AsyncURB AsyncURB; + +struct endp_data { + uint8_t halted; + uint8_t iso_started; + AsyncURB *iso_urb; + int iso_urb_idx; + int iso_buffer_used; + int inflight; +}; + +struct USBAutoFilter { + uint32_t bus_num; + uint32_t addr; + char *port; + uint32_t vendor_id; + uint32_t product_id; +}; + +typedef struct USBHostDevice { + USBDevice dev; + int fd; + int hub_fd; + int hub_port; + + uint8_t descr[8192]; + int descr_len; + int closing; + uint32_t iso_urb_count; + Notifier exit; + + struct endp_data ep_in[USB_MAX_ENDPOINTS]; + struct endp_data ep_out[USB_MAX_ENDPOINTS]; + QLIST_HEAD(, AsyncURB) aurbs; + + /* Host side address */ + int bus_num; + int addr; + char port[MAX_PORTLEN]; + struct USBAutoFilter match; + int seen, errcount; + + QTAILQ_ENTRY(USBHostDevice) next; +} USBHostDevice; + +static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs); + +static int usb_host_close(USBHostDevice *dev); +static int parse_filter(const char *spec, struct USBAutoFilter *f); +static void usb_host_auto_check(void *unused); +static int usb_host_read_file(char *line, size_t line_size, + const char *device_file, const char *device_name); +static int usb_linux_update_endp_table(USBHostDevice *s); + +static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p) +{ + static const int usbfs[] = { + [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL, + [USB_ENDPOINT_XFER_ISOC] = USBDEVFS_URB_TYPE_ISO, + [USB_ENDPOINT_XFER_BULK] = USBDEVFS_URB_TYPE_BULK, + [USB_ENDPOINT_XFER_INT] = USBDEVFS_URB_TYPE_INTERRUPT, + }; + uint8_t type = p->ep->type; + assert(type < ARRAY_SIZE(usbfs)); + return usbfs[type]; +} + +static int usb_host_do_reset(USBHostDevice *dev) +{ + struct timeval s, e; + uint32_t usecs; + int ret; + + gettimeofday(&s, NULL); + ret = ioctl(dev->fd, USBDEVFS_RESET); + gettimeofday(&e, NULL); + usecs = (e.tv_sec - s.tv_sec) * 1000000; + usecs += e.tv_usec - s.tv_usec; + if (usecs > 1000000) { + /* more than a second, something is fishy, broken usb device? */ + fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n", + dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000); + } + return ret; +} + +static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep) +{ + struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out; + assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); + assert(ep > 0 && ep <= USB_MAX_ENDPOINTS); + return eps + ep - 1; +} + +static int is_isoc(USBHostDevice *s, int pid, int ep) +{ + return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC; +} + +static int is_valid(USBHostDevice *s, int pid, int ep) +{ + return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID; +} + +static int is_halted(USBHostDevice *s, int pid, int ep) +{ + return get_endp(s, pid, ep)->halted; +} + +static void clear_halt(USBHostDevice *s, int pid, int ep) +{ + trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep); + get_endp(s, pid, ep)->halted = 0; +} + +static void set_halt(USBHostDevice *s, int pid, int ep) +{ + if (ep != 0) { + trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep); + get_endp(s, pid, ep)->halted = 1; + } +} + +static int is_iso_started(USBHostDevice *s, int pid, int ep) +{ + return get_endp(s, pid, ep)->iso_started; +} + +static void clear_iso_started(USBHostDevice *s, int pid, int ep) +{ + trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep); + get_endp(s, pid, ep)->iso_started = 0; +} + +static void set_iso_started(USBHostDevice *s, int pid, int ep) +{ + struct endp_data *e = get_endp(s, pid, ep); + + trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep); + if (!e->iso_started) { + e->iso_started = 1; + e->inflight = 0; + } +} + +static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value) +{ + struct endp_data *e = get_endp(s, pid, ep); + + e->inflight += value; + return e->inflight; +} + +static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb) +{ + get_endp(s, pid, ep)->iso_urb = iso_urb; +} + +static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep) +{ + return get_endp(s, pid, ep)->iso_urb; +} + +static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i) +{ + get_endp(s, pid, ep)->iso_urb_idx = i; +} + +static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep) +{ + return get_endp(s, pid, ep)->iso_urb_idx; +} + +static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i) +{ + get_endp(s, pid, ep)->iso_buffer_used = i; +} + +static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep) +{ + return get_endp(s, pid, ep)->iso_buffer_used; +} + +/* + * Async URB state. + * We always allocate iso packet descriptors even for bulk transfers + * to simplify allocation and casts. + */ +struct AsyncURB +{ + struct usbdevfs_urb urb; + struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB]; + USBHostDevice *hdev; + QLIST_ENTRY(AsyncURB) next; + + /* For regular async urbs */ + USBPacket *packet; + int more; /* large transfer, more urbs follow */ + + /* For buffered iso handling */ + int iso_frame_idx; /* -1 means in flight */ +}; + +static AsyncURB *async_alloc(USBHostDevice *s) +{ + AsyncURB *aurb = g_malloc0(sizeof(AsyncURB)); + aurb->hdev = s; + QLIST_INSERT_HEAD(&s->aurbs, aurb, next); + return aurb; +} + +static void async_free(AsyncURB *aurb) +{ + QLIST_REMOVE(aurb, next); + g_free(aurb); +} + +static void do_disconnect(USBHostDevice *s) +{ + usb_host_close(s); + usb_host_auto_check(NULL); +} + +static void async_complete(void *opaque) +{ + USBHostDevice *s = opaque; + AsyncURB *aurb; + int urbs = 0; + + while (1) { + USBPacket *p; + + int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb); + if (r < 0) { + if (errno == EAGAIN) { + if (urbs > 2) { + fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs); + } + return; + } + if (errno == ENODEV) { + if (!s->closing) { + trace_usb_host_disconnect(s->bus_num, s->addr); + do_disconnect(s); + } + return; + } + + perror("USBDEVFS_REAPURBNDELAY"); + return; + } + + DPRINTF("husb: async completed. aurb %p status %d alen %d\n", + aurb, aurb->urb.status, aurb->urb.actual_length); + + /* If this is a buffered iso urb mark it as complete and don't do + anything else (it is handled further in usb_host_handle_iso_data) */ + if (aurb->iso_frame_idx == -1) { + int inflight; + int pid = (aurb->urb.endpoint & USB_DIR_IN) ? + USB_TOKEN_IN : USB_TOKEN_OUT; + int ep = aurb->urb.endpoint & 0xf; + if (aurb->urb.status == -EPIPE) { + set_halt(s, pid, ep); + } + aurb->iso_frame_idx = 0; + urbs++; + inflight = change_iso_inflight(s, pid, ep, -1); + if (inflight == 0 && is_iso_started(s, pid, ep)) { + fprintf(stderr, "husb: out of buffers for iso stream\n"); + } + continue; + } + + p = aurb->packet; + trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status, + aurb->urb.actual_length, aurb->more); + + if (p) { + switch (aurb->urb.status) { + case 0: + p->result += aurb->urb.actual_length; + break; + + case -EPIPE: + set_halt(s, p->pid, p->ep->nr); + p->result = USB_RET_STALL; + break; + + case -EOVERFLOW: + p->result = USB_RET_BABBLE; + break; + + default: + p->result = USB_RET_IOERROR; + break; + } + + if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { + trace_usb_host_req_complete(s->bus_num, s->addr, p->result); + usb_generic_async_ctrl_complete(&s->dev, p); + } else if (!aurb->more) { + trace_usb_host_req_complete(s->bus_num, s->addr, p->result); + usb_packet_complete(&s->dev, p); + } + } + + async_free(aurb); + } +} + +static void usb_host_async_cancel(USBDevice *dev, USBPacket *p) +{ + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + AsyncURB *aurb; + + QLIST_FOREACH(aurb, &s->aurbs, next) { + if (p != aurb->packet) { + continue; + } + + DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb); + + /* Mark it as dead (see async_complete above) */ + aurb->packet = NULL; + + int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb); + if (r < 0) { + DPRINTF("husb: async. discard urb failed errno %d\n", errno); + } + } +} + +static int usb_host_open_device(int bus, int addr) +{ + const char *usbfs = NULL; + char filename[32]; + struct stat st; + int fd, rc; + + rc = stat("/dev/bus/usb", &st); + if (rc == 0 && S_ISDIR(st.st_mode)) { + /* udev-created device nodes available */ + usbfs = "/dev/bus/usb"; + } else { + /* fallback: usbfs mounted below /proc */ + usbfs = "/proc/bus/usb"; + } + + snprintf(filename, sizeof(filename), "%s/%03d/%03d", + usbfs, bus, addr); + fd = open(filename, O_RDWR | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno)); + } + return fd; +} + +static int usb_host_claim_port(USBHostDevice *s) +{ +#ifdef USBDEVFS_CLAIM_PORT + char *h, hub_name[64], line[1024]; + int hub_addr, ret; + + snprintf(hub_name, sizeof(hub_name), "%d-%s", + s->match.bus_num, s->match.port); + + /* try strip off last ".$portnr" to get hub */ + h = strrchr(hub_name, '.'); + if (h != NULL) { + s->hub_port = atoi(h+1); + *h = '\0'; + } else { + /* no dot in there -> it is the root hub */ + snprintf(hub_name, sizeof(hub_name), "usb%d", + s->match.bus_num); + s->hub_port = atoi(s->match.port); + } + + if (!usb_host_read_file(line, sizeof(line), "devnum", + hub_name)) { + return -1; + } + if (sscanf(line, "%d", &hub_addr) != 1) { + return -1; + } + + s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr); + if (s->hub_fd < 0) { + return -1; + } + + ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port); + if (ret < 0) { + close(s->hub_fd); + s->hub_fd = -1; + return -1; + } + + trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port); + return 0; +#else + return -1; +#endif +} + +static void usb_host_release_port(USBHostDevice *s) +{ + if (s->hub_fd == -1) { + return; + } +#ifdef USBDEVFS_RELEASE_PORT + ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port); +#endif + close(s->hub_fd); + s->hub_fd = -1; +} + +static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces) +{ + /* earlier Linux 2.4 do not support that */ +#ifdef USBDEVFS_DISCONNECT + struct usbdevfs_ioctl ctrl; + int ret, interface; + + for (interface = 0; interface < nb_interfaces; interface++) { + ctrl.ioctl_code = USBDEVFS_DISCONNECT; + ctrl.ifno = interface; + ctrl.data = 0; + ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl); + if (ret < 0 && errno != ENODATA) { + perror("USBDEVFS_DISCONNECT"); + return -1; + } + } +#endif + return 0; +} + +static int usb_linux_get_num_interfaces(USBHostDevice *s) +{ + char device_name[64], line[1024]; + int num_interfaces = 0; + + sprintf(device_name, "%d-%s", s->bus_num, s->port); + if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces", + device_name)) { + return -1; + } + if (sscanf(line, "%d", &num_interfaces) != 1) { + return -1; + } + return num_interfaces; +} + +static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) +{ + const char *op = NULL; + int dev_descr_len, config_descr_len; + int interface, nb_interfaces; + int ret, i; + + for (i = 0; i < USB_MAX_INTERFACES; i++) { + dev->dev.altsetting[i] = 0; + } + + if (configuration == 0) { /* address state - ignore */ + dev->dev.ninterfaces = 0; + dev->dev.configuration = 0; + return 1; + } + + DPRINTF("husb: claiming interfaces. config %d\n", configuration); + + i = 0; + dev_descr_len = dev->descr[0]; + if (dev_descr_len > dev->descr_len) { + fprintf(stderr, "husb: update iface failed. descr too short\n"); + return 0; + } + + i += dev_descr_len; + while (i < dev->descr_len) { + DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n", + i, dev->descr_len, + dev->descr[i], dev->descr[i+1]); + + if (dev->descr[i+1] != USB_DT_CONFIG) { + i += dev->descr[i]; + continue; + } + config_descr_len = dev->descr[i]; + + DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration); + + if (configuration == dev->descr[i + 5]) { + configuration = dev->descr[i + 5]; + break; + } + + i += config_descr_len; + } + + if (i >= dev->descr_len) { + fprintf(stderr, + "husb: update iface failed. no matching configuration\n"); + return 0; + } + nb_interfaces = dev->descr[i + 4]; + + if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) { + goto fail; + } + + /* XXX: only grab if all interfaces are free */ + for (interface = 0; interface < nb_interfaces; interface++) { + op = "USBDEVFS_CLAIMINTERFACE"; + ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface); + if (ret < 0) { + goto fail; + } + } + + trace_usb_host_claim_interfaces(dev->bus_num, dev->addr, + nb_interfaces, configuration); + + dev->dev.ninterfaces = nb_interfaces; + dev->dev.configuration = configuration; + return 1; + +fail: + if (errno == ENODEV) { + do_disconnect(dev); + } + perror(op); + return 0; +} + +static int usb_host_release_interfaces(USBHostDevice *s) +{ + int ret, i; + + trace_usb_host_release_interfaces(s->bus_num, s->addr); + + for (i = 0; i < s->dev.ninterfaces; i++) { + ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i); + if (ret < 0) { + perror("USBDEVFS_RELEASEINTERFACE"); + return 0; + } + } + return 1; +} + +static void usb_host_handle_reset(USBDevice *dev) +{ + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + + trace_usb_host_reset(s->bus_num, s->addr); + + usb_host_do_reset(s);; + + usb_host_claim_interfaces(s, 0); + usb_linux_update_endp_table(s); +} + +static void usb_host_handle_destroy(USBDevice *dev) +{ + USBHostDevice *s = (USBHostDevice *)dev; + + usb_host_release_port(s); + usb_host_close(s); + QTAILQ_REMOVE(&hostdevs, s, next); + qemu_remove_exit_notifier(&s->exit); +} + +/* iso data is special, we need to keep enough urbs in flight to make sure + that the controller never runs out of them, otherwise the device will + likely suffer a buffer underrun / overrun. */ +static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep) +{ + AsyncURB *aurb; + int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep); + + aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb)); + for (i = 0; i < s->iso_urb_count; i++) { + aurb[i].urb.endpoint = ep; + aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len; + aurb[i].urb.buffer = g_malloc(aurb[i].urb.buffer_length); + aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO; + aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP; + aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB; + for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++) + aurb[i].urb.iso_frame_desc[j].length = len; + if (pid == USB_TOKEN_IN) { + aurb[i].urb.endpoint |= 0x80; + /* Mark as fully consumed (idle) */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB; + } + } + set_iso_urb(s, pid, ep, aurb); + + return aurb; +} + +static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep) +{ + AsyncURB *aurb; + int i, ret, killed = 0, free = 1; + + aurb = get_iso_urb(s, pid, ep); + if (!aurb) { + return; + } + + for (i = 0; i < s->iso_urb_count; i++) { + /* in flight? */ + if (aurb[i].iso_frame_idx == -1) { + ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]); + if (ret < 0) { + perror("USBDEVFS_DISCARDURB"); + free = 0; + continue; + } + killed++; + } + } + + /* Make sure any urbs we've killed are reaped before we free them */ + if (killed) { + async_complete(s); + } + + for (i = 0; i < s->iso_urb_count; i++) { + g_free(aurb[i].urb.buffer); + } + + if (free) + g_free(aurb); + else + printf("husb: leaking iso urbs because of discard failure\n"); + set_iso_urb(s, pid, ep, NULL); + set_iso_urb_idx(s, pid, ep, 0); + clear_iso_started(s, pid, ep); +} + +static int urb_status_to_usb_ret(int status) +{ + switch (status) { + case -EPIPE: + return USB_RET_STALL; + case -EOVERFLOW: + return USB_RET_BABBLE; + default: + return USB_RET_IOERROR; + } +} + +static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) +{ + AsyncURB *aurb; + int i, j, ret, max_packet_size, offset, len = 0; + uint8_t *buf; + + max_packet_size = p->ep->max_packet_size; + if (max_packet_size == 0) + return USB_RET_NAK; + + aurb = get_iso_urb(s, p->pid, p->ep->nr); + if (!aurb) { + aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr); + } + + i = get_iso_urb_idx(s, p->pid, p->ep->nr); + j = aurb[i].iso_frame_idx; + if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { + if (in) { + /* Check urb status */ + if (aurb[i].urb.status) { + len = urb_status_to_usb_ret(aurb[i].urb.status); + /* Move to the next urb */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; + /* Check frame status */ + } else if (aurb[i].urb.iso_frame_desc[j].status) { + len = urb_status_to_usb_ret( + aurb[i].urb.iso_frame_desc[j].status); + /* Check the frame fits */ + } else if (aurb[i].urb.iso_frame_desc[j].actual_length + > p->iov.size) { + printf("husb: received iso data is larger then packet\n"); + len = USB_RET_BABBLE; + /* All good copy data over */ + } else { + len = aurb[i].urb.iso_frame_desc[j].actual_length; + buf = aurb[i].urb.buffer + + j * aurb[i].urb.iso_frame_desc[0].length; + usb_packet_copy(p, buf, len); + } + } else { + len = p->iov.size; + offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr); + + /* Check the frame fits */ + if (len > max_packet_size) { + printf("husb: send iso data is larger then max packet size\n"); + return USB_RET_NAK; + } + + /* All good copy data over */ + usb_packet_copy(p, aurb[i].urb.buffer + offset, len); + aurb[i].urb.iso_frame_desc[j].length = len; + offset += len; + set_iso_buffer_used(s, p->pid, p->ep->nr, offset); + + /* Start the stream once we have buffered enough data */ + if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) { + set_iso_started(s, p->pid, p->ep->nr); + } + } + aurb[i].iso_frame_idx++; + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + i = (i + 1) % s->iso_urb_count; + set_iso_urb_idx(s, p->pid, p->ep->nr, i); + } + } else { + if (in) { + set_iso_started(s, p->pid, p->ep->nr); + } else { + DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); + } + } + + if (is_iso_started(s, p->pid, p->ep->nr)) { + /* (Re)-submit all fully consumed / filled urbs */ + for (i = 0; i < s->iso_urb_count; i++) { + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); + if (ret < 0) { + perror("USBDEVFS_SUBMITURB"); + if (!in || len == 0) { + switch(errno) { + case ETIMEDOUT: + len = USB_RET_NAK; + break; + case EPIPE: + default: + len = USB_RET_STALL; + } + } + break; + } + aurb[i].iso_frame_idx = -1; + change_iso_inflight(s, p->pid, p->ep->nr, 1); + } + } + } + + return len; +} + +static int usb_host_handle_data(USBDevice *dev, USBPacket *p) +{ + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + struct usbdevfs_urb *urb; + AsyncURB *aurb; + int ret, rem, prem, v; + uint8_t *pbuf; + uint8_t ep; + + trace_usb_host_req_data(s->bus_num, s->addr, + p->pid == USB_TOKEN_IN, + p->ep->nr, p->iov.size); + + if (!is_valid(s, p->pid, p->ep->nr)) { + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); + return USB_RET_NAK; + } + + if (p->pid == USB_TOKEN_IN) { + ep = p->ep->nr | 0x80; + } else { + ep = p->ep->nr; + } + + if (is_halted(s, p->pid, p->ep->nr)) { + unsigned int arg = ep; + ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg); + if (ret < 0) { + perror("USBDEVFS_CLEAR_HALT"); + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); + return USB_RET_NAK; + } + clear_halt(s, p->pid, p->ep->nr); + } + + if (is_isoc(s, p->pid, p->ep->nr)) { + return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); + } + + v = 0; + prem = p->iov.iov[v].iov_len; + pbuf = p->iov.iov[v].iov_base; + rem = p->iov.size; + while (rem) { + if (prem == 0) { + v++; + assert(v < p->iov.niov); + prem = p->iov.iov[v].iov_len; + pbuf = p->iov.iov[v].iov_base; + assert(prem <= rem); + } + aurb = async_alloc(s); + aurb->packet = p; + + urb = &aurb->urb; + urb->endpoint = ep; + urb->type = usb_host_usbfs_type(s, p); + urb->usercontext = s; + urb->buffer = pbuf; + urb->buffer_length = prem; + + if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) { + urb->buffer_length = MAX_USBFS_BUFFER_SIZE; + } + pbuf += urb->buffer_length; + prem -= urb->buffer_length; + rem -= urb->buffer_length; + if (rem) { + aurb->more = 1; + } + + trace_usb_host_urb_submit(s->bus_num, s->addr, aurb, + urb->buffer_length, aurb->more); + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); + + DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n", + urb->endpoint, urb->buffer_length, aurb->more, p, aurb); + + if (ret < 0) { + perror("USBDEVFS_SUBMITURB"); + async_free(aurb); + + switch(errno) { + case ETIMEDOUT: + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); + return USB_RET_NAK; + case EPIPE: + default: + trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL); + return USB_RET_STALL; + } + } + } + + return USB_RET_ASYNC; +} + +static int ctrl_error(void) +{ + if (errno == ETIMEDOUT) { + return USB_RET_NAK; + } else { + return USB_RET_STALL; + } +} + +static int usb_host_set_address(USBHostDevice *s, int addr) +{ + trace_usb_host_set_address(s->bus_num, s->addr, addr); + s->dev.addr = addr; + return 0; +} + +static int usb_host_set_config(USBHostDevice *s, int config) +{ + int ret, first = 1; + + trace_usb_host_set_config(s->bus_num, s->addr, config); + + usb_host_release_interfaces(s); + +again: + ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config); + + DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno); + + if (ret < 0 && errno == EBUSY && first) { + /* happens if usb device is in use by host drivers */ + int count = usb_linux_get_num_interfaces(s); + if (count > 0) { + DPRINTF("husb: busy -> disconnecting %d interfaces\n", count); + usb_host_disconnect_ifaces(s, count); + first = 0; + goto again; + } + } + + if (ret < 0) { + return ctrl_error(); + } + usb_host_claim_interfaces(s, config); + usb_linux_update_endp_table(s); + return 0; +} + +static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) +{ + struct usbdevfs_setinterface si; + int i, ret; + + trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt); + + for (i = 1; i <= USB_MAX_ENDPOINTS; i++) { + if (is_isoc(s, USB_TOKEN_IN, i)) { + usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i); + } + if (is_isoc(s, USB_TOKEN_OUT, i)) { + usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i); + } + } + + if (iface >= USB_MAX_INTERFACES) { + return USB_RET_STALL; + } + + si.interface = iface; + si.altsetting = alt; + ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si); + + DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n", + iface, alt, ret, errno); + + if (ret < 0) { + return ctrl_error(); + } + + s->dev.altsetting[iface] = alt; + usb_linux_update_endp_table(s); + return 0; +} + +static int usb_host_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) +{ + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + struct usbdevfs_urb *urb; + AsyncURB *aurb; + int ret; + + /* + * Process certain standard device requests. + * These are infrequent and are processed synchronously. + */ + + /* Note request is (bRequestType << 8) | bRequest */ + trace_usb_host_req_control(s->bus_num, s->addr, request, value, index); + + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + return usb_host_set_address(s, value); + + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + return usb_host_set_config(s, value & 0xff); + + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + return usb_host_set_interface(s, index, value); + } + + /* The rest are asynchronous */ + + if (length > sizeof(dev->data_buf)) { + fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", + length, sizeof(dev->data_buf)); + return USB_RET_STALL; + } + + aurb = async_alloc(s); + aurb->packet = p; + + /* + * Setup ctrl transfer. + * + * s->ctrl is laid out such that data buffer immediately follows + * 'req' struct which is exactly what usbdevfs expects. + */ + urb = &aurb->urb; + + urb->type = USBDEVFS_URB_TYPE_CONTROL; + urb->endpoint = p->ep->nr; + + urb->buffer = &dev->setup_buf; + urb->buffer_length = length + 8; + + urb->usercontext = s; + + trace_usb_host_urb_submit(s->bus_num, s->addr, aurb, + urb->buffer_length, aurb->more); + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); + + DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb); + + if (ret < 0) { + DPRINTF("husb: submit failed. errno %d\n", errno); + async_free(aurb); + + switch(errno) { + case ETIMEDOUT: + return USB_RET_NAK; + case EPIPE: + default: + return USB_RET_STALL; + } + } + + return USB_RET_ASYNC; +} + +static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, + uint8_t configuration, uint8_t interface) +{ + char device_name[64], line[1024]; + int alt_setting; + + sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port, + (int)configuration, (int)interface); + + if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting", + device_name)) { + /* Assume alt 0 on error */ + return 0; + } + if (sscanf(line, "%d", &alt_setting) != 1) { + /* Assume alt 0 on error */ + return 0; + } + return alt_setting; +} + +/* returns 1 on problem encountered or 0 for success */ +static int usb_linux_update_endp_table(USBHostDevice *s) +{ + uint8_t *descriptors; + uint8_t devep, type, alt_interface; + uint16_t raw; + int interface, length, i, ep, pid; + struct endp_data *epd; + + usb_ep_init(&s->dev); + + if (s->dev.configuration == 0) { + /* not configured yet -- leave all endpoints disabled */ + return 0; + } + + /* get the desired configuration, interface, and endpoint descriptors + * from device description */ + descriptors = &s->descr[18]; + length = s->descr_len - 18; + i = 0; + + while (i < length) { + if (descriptors[i + 1] != USB_DT_CONFIG) { + fprintf(stderr, "invalid descriptor data\n"); + return 1; + } else if (descriptors[i + 5] != s->dev.configuration) { + DPRINTF("not requested configuration %d\n", s->dev.configuration); + i += (descriptors[i + 3] << 8) + descriptors[i + 2]; + continue; + } + i += descriptors[i]; + + if (descriptors[i + 1] != USB_DT_INTERFACE || + (descriptors[i + 1] == USB_DT_INTERFACE && + descriptors[i + 4] == 0)) { + i += descriptors[i]; + continue; + } + + interface = descriptors[i + 2]; + alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration, + interface); + + /* the current interface descriptor is the active interface + * and has endpoints */ + if (descriptors[i + 3] != alt_interface) { + i += descriptors[i]; + continue; + } + + /* advance to the endpoints */ + while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) { + i += descriptors[i]; + } + + if (i >= length) + break; + + while (i < length) { + if (descriptors[i + 1] != USB_DT_ENDPOINT) { + break; + } + + devep = descriptors[i + 2]; + pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; + ep = devep & 0xf; + if (ep == 0) { + fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n"); + return 1; + } + + type = descriptors[i + 3] & 0x3; + raw = descriptors[i + 4] + (descriptors[i + 5] << 8); + usb_ep_set_max_packet_size(&s->dev, pid, ep, raw); + assert(usb_ep_get_type(&s->dev, pid, ep) == + USB_ENDPOINT_XFER_INVALID); + usb_ep_set_type(&s->dev, pid, ep, type); + usb_ep_set_ifnum(&s->dev, pid, ep, interface); + if (type == USB_ENDPOINT_XFER_BULK) { + usb_ep_set_pipeline(&s->dev, pid, ep, true); + } + + epd = get_endp(s, pid, ep); + epd->halted = 0; + + i += descriptors[i]; + } + } +#ifdef DEBUG + usb_ep_dump(&s->dev); +#endif + return 0; +} + +/* + * Check if we can safely redirect a usb2 device to a usb1 virtual controller, + * this function assumes this is safe, if: + * 1) There are no isoc endpoints + * 2) There are no interrupt endpoints with a max_packet_size > 64 + * Note bulk endpoints with a max_packet_size > 64 in theory also are not + * usb1 compatible, but in practice this seems to work fine. + */ +static int usb_linux_full_speed_compat(USBHostDevice *dev) +{ + int i, packet_size; + + /* + * usb_linux_update_endp_table only registers info about ep in the current + * interface altsettings, so we need to parse the descriptors again. + */ + for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) { + if (dev->descr[i + 1] == USB_DT_ENDPOINT) { + switch (dev->descr[i + 3] & 0x3) { + case 0x00: /* CONTROL */ + break; + case 0x01: /* ISO */ + return 0; + case 0x02: /* BULK */ + break; + case 0x03: /* INTERRUPT */ + packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8); + if (packet_size > 64) + return 0; + break; + } + } + } + return 1; +} + +static int usb_host_open(USBHostDevice *dev, int bus_num, + int addr, const char *port, + const char *prod_name, int speed) +{ + int fd = -1, ret; + + trace_usb_host_open_started(bus_num, addr); + + if (dev->fd != -1) { + goto fail; + } + + fd = usb_host_open_device(bus_num, addr); + if (fd < 0) { + goto fail; + } + DPRINTF("husb: opened %s\n", buf); + + dev->bus_num = bus_num; + dev->addr = addr; + strcpy(dev->port, port); + dev->fd = fd; + + /* read the device description */ + dev->descr_len = read(fd, dev->descr, sizeof(dev->descr)); + if (dev->descr_len <= 0) { + perror("husb: reading device data failed"); + goto fail; + } + +#ifdef DEBUG + { + int x; + printf("=== begin dumping device descriptor data ===\n"); + for (x = 0; x < dev->descr_len; x++) { + printf("%02x ", dev->descr[x]); + } + printf("\n=== end dumping device descriptor data ===\n"); + } +#endif + + + /* start unconfigured -- we'll wait for the guest to set a configuration */ + if (!usb_host_claim_interfaces(dev, 0)) { + goto fail; + } + + ret = usb_linux_update_endp_table(dev); + if (ret) { + goto fail; + } + + if (speed == -1) { + struct usbdevfs_connectinfo ci; + + ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); + if (ret < 0) { + perror("usb_host_device_open: USBDEVFS_CONNECTINFO"); + goto fail; + } + + if (ci.slow) { + speed = USB_SPEED_LOW; + } else { + speed = USB_SPEED_HIGH; + } + } + dev->dev.speed = speed; + dev->dev.speedmask = (1 << speed); + if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) { + dev->dev.speedmask |= USB_SPEED_MASK_FULL; + } + + trace_usb_host_open_success(bus_num, addr); + + if (!prod_name || prod_name[0] == '\0') { + snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), + "host:%d.%d", bus_num, addr); + } else { + pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), + prod_name); + } + + ret = usb_device_attach(&dev->dev); + if (ret) { + goto fail; + } + + /* USB devio uses 'write' flag to check for async completions */ + qemu_set_fd_handler(dev->fd, NULL, async_complete, dev); + + return 0; + +fail: + trace_usb_host_open_failure(bus_num, addr); + if (dev->fd != -1) { + close(dev->fd); + dev->fd = -1; + } + return -1; +} + +static int usb_host_close(USBHostDevice *dev) +{ + int i; + + if (dev->fd == -1) { + return -1; + } + + trace_usb_host_close(dev->bus_num, dev->addr); + + qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); + dev->closing = 1; + for (i = 1; i <= USB_MAX_ENDPOINTS; i++) { + if (is_isoc(dev, USB_TOKEN_IN, i)) { + usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i); + } + if (is_isoc(dev, USB_TOKEN_OUT, i)) { + usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i); + } + } + async_complete(dev); + dev->closing = 0; + if (dev->dev.attached) { + usb_device_detach(&dev->dev); + } + usb_host_do_reset(dev); + close(dev->fd); + dev->fd = -1; + return 0; +} + +static void usb_host_exit_notifier(struct Notifier *n, void *data) +{ + USBHostDevice *s = container_of(n, USBHostDevice, exit); + + usb_host_release_port(s); + if (s->fd != -1) { + usb_host_do_reset(s);; + } +} + +static int usb_host_initfn(USBDevice *dev) +{ + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + + dev->auto_attach = 0; + s->fd = -1; + s->hub_fd = -1; + + QTAILQ_INSERT_TAIL(&hostdevs, s, next); + s->exit.notify = usb_host_exit_notifier; + qemu_add_exit_notifier(&s->exit); + usb_host_auto_check(NULL); + + if (s->match.bus_num != 0 && s->match.port != NULL) { + usb_host_claim_port(s); + } + return 0; +} + +static const VMStateDescription vmstate_usb_host = { + .name = "usb-host", + .unmigratable = 1, +}; + +static Property usb_host_dev_properties[] = { + DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0), + DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0), + DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), + DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0), + DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0), + DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), + DEFINE_PROP_END_OF_LIST(), +}; + +static void usb_host_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->init = usb_host_initfn; + uc->product_desc = "USB Host Device"; + uc->cancel_packet = usb_host_async_cancel; + uc->handle_data = usb_host_handle_data; + uc->handle_control = usb_host_handle_control; + uc->handle_reset = usb_host_handle_reset; + uc->handle_destroy = usb_host_handle_destroy; + dc->vmsd = &vmstate_usb_host; + dc->props = usb_host_dev_properties; +} + +static TypeInfo usb_host_dev_info = { + .name = "usb-host", + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(USBHostDevice), + .class_init = usb_host_class_initfn, +}; + +static void usb_host_register_types(void) +{ + type_register_static(&usb_host_dev_info); + usb_legacy_register("usb-host", "host", usb_host_device_open); +} + +type_init(usb_host_register_types) + +USBDevice *usb_host_device_open(USBBus *bus, const char *devname) +{ + struct USBAutoFilter filter; + USBDevice *dev; + char *p; + + dev = usb_create(bus, "usb-host"); + + if (strstr(devname, "auto:")) { + if (parse_filter(devname, &filter) < 0) { + goto fail; + } + } else { + if ((p = strchr(devname, '.'))) { + filter.bus_num = strtoul(devname, NULL, 0); + filter.addr = strtoul(p + 1, NULL, 0); + filter.vendor_id = 0; + filter.product_id = 0; + } else if ((p = strchr(devname, ':'))) { + filter.bus_num = 0; + filter.addr = 0; + filter.vendor_id = strtoul(devname, NULL, 16); + filter.product_id = strtoul(p + 1, NULL, 16); + } else { + goto fail; + } + } + + qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num); + qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr); + qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id); + qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id); + qdev_init_nofail(&dev->qdev); + return dev; + +fail: + qdev_free(&dev->qdev); + return NULL; +} + +int usb_host_device_close(const char *devname) +{ +#if 0 + char product_name[PRODUCT_NAME_SZ]; + int bus_num, addr; + USBHostDevice *s; + + if (strstr(devname, "auto:")) { + return usb_host_auto_del(devname); + } + if (usb_host_find_device(&bus_num, &addr, product_name, + sizeof(product_name), devname) < 0) { + return -1; + } + s = hostdev_find(bus_num, addr); + if (s) { + usb_device_delete_addr(s->bus_num, s->dev.addr); + return 0; + } +#endif + + return -1; +} + +/* + * Read sys file-system device file + * + * @line address of buffer to put file contents in + * @line_size size of line + * @device_file path to device file (printf format string) + * @device_name device being opened (inserted into device_file) + * + * @return 0 failed, 1 succeeded ('line' contains data) + */ +static int usb_host_read_file(char *line, size_t line_size, + const char *device_file, const char *device_name) +{ + FILE *f; + int ret = 0; + char filename[PATH_MAX]; + + snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name, + device_file); + f = fopen(filename, "r"); + if (f) { + ret = fgets(line, line_size, f) != NULL; + fclose(f); + } + + return ret; +} + +/* + * Use /sys/bus/usb/devices/ directory to determine host's USB + * devices. + * + * This code is based on Robert Schiele's original patches posted to + * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950 + */ +static int usb_host_scan(void *opaque, USBScanFunc *func) +{ + DIR *dir = NULL; + char line[1024]; + int bus_num, addr, speed, class_id, product_id, vendor_id; + int ret = 0; + char port[MAX_PORTLEN]; + char product_name[512]; + struct dirent *de; + + dir = opendir("/sys/bus/usb/devices"); + if (!dir) { + perror("husb: opendir /sys/bus/usb/devices"); + fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n"); + goto the_end; + } + + while ((de = readdir(dir))) { + if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) { + if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) { + continue; + } + + if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) { + goto the_end; + } + if (sscanf(line, "%d", &addr) != 1) { + goto the_end; + } + if (!usb_host_read_file(line, sizeof(line), "bDeviceClass", + de->d_name)) { + goto the_end; + } + if (sscanf(line, "%x", &class_id) != 1) { + goto the_end; + } + + if (!usb_host_read_file(line, sizeof(line), "idVendor", + de->d_name)) { + goto the_end; + } + if (sscanf(line, "%x", &vendor_id) != 1) { + goto the_end; + } + if (!usb_host_read_file(line, sizeof(line), "idProduct", + de->d_name)) { + goto the_end; + } + if (sscanf(line, "%x", &product_id) != 1) { + goto the_end; + } + if (!usb_host_read_file(line, sizeof(line), "product", + de->d_name)) { + *product_name = 0; + } else { + if (strlen(line) > 0) { + line[strlen(line) - 1] = '\0'; + } + pstrcpy(product_name, sizeof(product_name), line); + } + + if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) { + goto the_end; + } + if (!strcmp(line, "5000\n")) { + speed = USB_SPEED_SUPER; + } else if (!strcmp(line, "480\n")) { + speed = USB_SPEED_HIGH; + } else if (!strcmp(line, "1.5\n")) { + speed = USB_SPEED_LOW; + } else { + speed = USB_SPEED_FULL; + } + + ret = func(opaque, bus_num, addr, port, class_id, vendor_id, + product_id, product_name, speed); + if (ret) { + goto the_end; + } + } + } + the_end: + if (dir) { + closedir(dir); + } + return ret; +} + +static QEMUTimer *usb_auto_timer; + +static int usb_host_auto_scan(void *opaque, int bus_num, + int addr, const char *port, + int class_id, int vendor_id, int product_id, + const char *product_name, int speed) +{ + struct USBAutoFilter *f; + struct USBHostDevice *s; + + /* Ignore hubs */ + if (class_id == 9) + return 0; + + QTAILQ_FOREACH(s, &hostdevs, next) { + f = &s->match; + + if (f->bus_num > 0 && f->bus_num != bus_num) { + continue; + } + if (f->addr > 0 && f->addr != addr) { + continue; + } + if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) { + continue; + } + + if (f->vendor_id > 0 && f->vendor_id != vendor_id) { + continue; + } + + if (f->product_id > 0 && f->product_id != product_id) { + continue; + } + /* We got a match */ + s->seen++; + if (s->errcount >= 3) { + return 0; + } + + /* Already attached ? */ + if (s->fd != -1) { + return 0; + } + DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); + + if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) { + s->errcount++; + } + break; + } + + return 0; +} + +static void usb_host_auto_check(void *unused) +{ + struct USBHostDevice *s; + int unconnected = 0; + + usb_host_scan(NULL, usb_host_auto_scan); + + QTAILQ_FOREACH(s, &hostdevs, next) { + if (s->fd == -1) { + unconnected++; + } + if (s->seen == 0) { + s->errcount = 0; + } + s->seen = 0; + } + + if (unconnected == 0) { + /* nothing to watch */ + if (usb_auto_timer) { + qemu_del_timer(usb_auto_timer); + trace_usb_host_auto_scan_disabled(); + } + return; + } + + if (!usb_auto_timer) { + usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL); + if (!usb_auto_timer) { + return; + } + trace_usb_host_auto_scan_enabled(); + } + qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); +} + +/* + * Autoconnect filter + * Format: + * auto:bus:dev[:vid:pid] + * auto:bus.dev[:vid:pid] + * + * bus - bus number (dec, * means any) + * dev - device number (dec, * means any) + * vid - vendor id (hex, * means any) + * pid - product id (hex, * means any) + * + * See 'lsusb' output. + */ +static int parse_filter(const char *spec, struct USBAutoFilter *f) +{ + enum { BUS, DEV, VID, PID, DONE }; + const char *p = spec; + int i; + + f->bus_num = 0; + f->addr = 0; + f->vendor_id = 0; + f->product_id = 0; + + for (i = BUS; i < DONE; i++) { + p = strpbrk(p, ":."); + if (!p) { + break; + } + p++; + + if (*p == '*') { + continue; + } + switch(i) { + case BUS: f->bus_num = strtol(p, NULL, 10); break; + case DEV: f->addr = strtol(p, NULL, 10); break; + case VID: f->vendor_id = strtol(p, NULL, 16); break; + case PID: f->product_id = strtol(p, NULL, 16); break; + } + } + + if (i < DEV) { + fprintf(stderr, "husb: invalid auto filter spec %s\n", spec); + return -1; + } + + return 0; +} + +/**********************/ +/* USB host device info */ + +struct usb_class_info { + int class; + const char *class_name; +}; + +static const struct usb_class_info usb_class_info[] = { + { USB_CLASS_AUDIO, "Audio"}, + { USB_CLASS_COMM, "Communication"}, + { USB_CLASS_HID, "HID"}, + { USB_CLASS_HUB, "Hub" }, + { USB_CLASS_PHYSICAL, "Physical" }, + { USB_CLASS_PRINTER, "Printer" }, + { USB_CLASS_MASS_STORAGE, "Storage" }, + { USB_CLASS_CDC_DATA, "Data" }, + { USB_CLASS_APP_SPEC, "Application Specific" }, + { USB_CLASS_VENDOR_SPEC, "Vendor Specific" }, + { USB_CLASS_STILL_IMAGE, "Still Image" }, + { USB_CLASS_CSCID, "Smart Card" }, + { USB_CLASS_CONTENT_SEC, "Content Security" }, + { -1, NULL } +}; + +static const char *usb_class_str(uint8_t class) +{ + const struct usb_class_info *p; + for(p = usb_class_info; p->class != -1; p++) { + if (p->class == class) { + break; + } + } + return p->class_name; +} + +static void usb_info_device(Monitor *mon, int bus_num, + int addr, const char *port, + int class_id, int vendor_id, int product_id, + const char *product_name, + int speed) +{ + const char *class_str, *speed_str; + + switch(speed) { + case USB_SPEED_LOW: + speed_str = "1.5"; + break; + case USB_SPEED_FULL: + speed_str = "12"; + break; + case USB_SPEED_HIGH: + speed_str = "480"; + break; + case USB_SPEED_SUPER: + speed_str = "5000"; + break; + default: + speed_str = "?"; + break; + } + + monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n", + bus_num, addr, port, speed_str); + class_str = usb_class_str(class_id); + if (class_str) { + monitor_printf(mon, " %s:", class_str); + } else { + monitor_printf(mon, " Class %02x:", class_id); + } + monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id); + if (product_name[0] != '\0') { + monitor_printf(mon, ", %s", product_name); + } + monitor_printf(mon, "\n"); +} + +static int usb_host_info_device(void *opaque, int bus_num, int addr, + const char *path, int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) +{ + Monitor *mon = opaque; + + usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id, + product_name, speed); + return 0; +} + +static void dec2str(int val, char *str, size_t size) +{ + if (val == 0) { + snprintf(str, size, "*"); + } else { + snprintf(str, size, "%d", val); + } +} + +static void hex2str(int val, char *str, size_t size) +{ + if (val == 0) { + snprintf(str, size, "*"); + } else { + snprintf(str, size, "%04x", val); + } +} + +void usb_host_info(Monitor *mon) +{ + struct USBAutoFilter *f; + struct USBHostDevice *s; + + usb_host_scan(mon, usb_host_info_device); + + if (QTAILQ_EMPTY(&hostdevs)) { + return; + } + + monitor_printf(mon, " Auto filters:\n"); + QTAILQ_FOREACH(s, &hostdevs, next) { + char bus[10], addr[10], vid[10], pid[10]; + f = &s->match; + dec2str(f->bus_num, bus, sizeof(bus)); + dec2str(f->addr, addr, sizeof(addr)); + hex2str(f->vendor_id, vid, sizeof(vid)); + hex2str(f->product_id, pid, sizeof(pid)); + monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n", + bus, addr, f->port ? f->port : "*", vid, pid); + } +} diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c new file mode 100644 index 0000000000..b4e10c12ca --- /dev/null +++ b/hw/usb/host-stub.c @@ -0,0 +1,52 @@ +/* + * Stub host USB redirector + * + * Copyright (c) 2005 Fabrice Bellard + * + * Copyright (c) 2008 Max Krasnyansky + * Support for host device auto connect & disconnect + * Major rewrite to support fully async operation + * + * Copyright 2008 TJ <linux@tjworld.net> + * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition + * to the legacy /proc/bus/usb USB device discovery and handling + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "console.h" +#include "hw/usb.h" +#include "monitor.h" + +void usb_host_info(Monitor *mon) +{ + monitor_printf(mon, "USB host devices not supported\n"); +} + +/* XXX: modify configure to compile the right host driver */ +USBDevice *usb_host_device_open(USBBus *bus, const char *devname) +{ + return NULL; +} + +int usb_host_device_close(const char *devname) +{ + return 0; +} diff --git a/hw/usb-libhw.c b/hw/usb/libhw.c index 162b42bd5b..2462351389 100644 --- a/hw/usb-libhw.c +++ b/hw/usb/libhw.c @@ -21,7 +21,7 @@ */ #include "qemu-common.h" #include "cpu-common.h" -#include "usb.h" +#include "hw/usb.h" #include "dma.h" int usb_packet_map(USBPacket *p, QEMUSGList *sgl) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c new file mode 100644 index 0000000000..8e9f175dbb --- /dev/null +++ b/hw/usb/redirect.c @@ -0,0 +1,1485 @@ +/* + * USB redirector usb-guest + * + * Copyright (c) 2011 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede <hdegoede@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu-timer.h" +#include "monitor.h" +#include "sysemu.h" + +#include <dirent.h> +#include <sys/ioctl.h> +#include <signal.h> +#include <usbredirparser.h> +#include <usbredirfilter.h> + +#include "hw/usb.h" + +#define MAX_ENDPOINTS 32 +#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) +#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) + +typedef struct AsyncURB AsyncURB; +typedef struct USBRedirDevice USBRedirDevice; + +/* Struct to hold buffered packets (iso or int input packets) */ +struct buf_packet { + uint8_t *data; + int len; + int status; + QTAILQ_ENTRY(buf_packet)next; +}; + +struct endp_data { + uint8_t type; + uint8_t interval; + uint8_t interface; /* bInterfaceNumber this ep belongs to */ + uint8_t iso_started; + uint8_t iso_error; /* For reporting iso errors to the HC */ + uint8_t interrupt_started; + uint8_t interrupt_error; + uint8_t bufpq_prefilled; + uint8_t bufpq_dropping_packets; + QTAILQ_HEAD(, buf_packet) bufpq; + int bufpq_size; + int bufpq_target_size; +}; + +struct USBRedirDevice { + USBDevice dev; + /* Properties */ + CharDriverState *cs; + uint8_t debug; + char *filter_str; + /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ + const uint8_t *read_buf; + int read_buf_size; + /* For async handling of open/close */ + QEMUBH *open_close_bh; + /* To delay the usb attach in case of quick chardev close + open */ + QEMUTimer *attach_timer; + int64_t next_attach_time; + struct usbredirparser *parser; + struct endp_data endpoint[MAX_ENDPOINTS]; + uint32_t packet_id; + QTAILQ_HEAD(, AsyncURB) asyncq; + /* Data for device filtering */ + struct usb_redir_device_connect_header device_info; + struct usb_redir_interface_info_header interface_info; + struct usbredirfilter_rule *filter_rules; + int filter_rules_count; +}; + +struct AsyncURB { + USBRedirDevice *dev; + USBPacket *packet; + uint32_t packet_id; + int get; + union { + struct usb_redir_control_packet_header control_packet; + struct usb_redir_bulk_packet_header bulk_packet; + struct usb_redir_interrupt_packet_header interrupt_packet; + }; + QTAILQ_ENTRY(AsyncURB)next; +}; + +static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); +static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect); +static void usbredir_device_disconnect(void *priv); +static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info); +static void usbredir_ep_info(void *priv, + struct usb_redir_ep_info_header *ep_info); +static void usbredir_configuration_status(void *priv, uint32_t id, + struct usb_redir_configuration_status_header *configuration_status); +static void usbredir_alt_setting_status(void *priv, uint32_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status); +static void usbredir_iso_stream_status(void *priv, uint32_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status); +static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status); +static void usbredir_bulk_streams_status(void *priv, uint32_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status); +static void usbredir_control_packet(void *priv, uint32_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len); +static void usbredir_bulk_packet(void *priv, uint32_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len); +static void usbredir_iso_packet(void *priv, uint32_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len); +static void usbredir_interrupt_packet(void *priv, uint32_t id, + struct usb_redir_interrupt_packet_header *interrupt_header, + uint8_t *data, int data_len); + +static int usbredir_handle_status(USBRedirDevice *dev, + int status, int actual_len); + +#define VERSION "qemu usb-redir guest " QEMU_VERSION + +/* + * Logging stuff + */ + +#define ERROR(...) \ + do { \ + if (dev->debug >= usbredirparser_error) { \ + error_report("usb-redir error: " __VA_ARGS__); \ + } \ + } while (0) +#define WARNING(...) \ + do { \ + if (dev->debug >= usbredirparser_warning) { \ + error_report("usb-redir warning: " __VA_ARGS__); \ + } \ + } while (0) +#define INFO(...) \ + do { \ + if (dev->debug >= usbredirparser_info) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) +#define DPRINTF(...) \ + do { \ + if (dev->debug >= usbredirparser_debug) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) +#define DPRINTF2(...) \ + do { \ + if (dev->debug >= usbredirparser_debug_data) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) + +static void usbredir_log(void *priv, int level, const char *msg) +{ + USBRedirDevice *dev = priv; + + if (dev->debug < level) { + return; + } + + error_report("%s", msg); +} + +static void usbredir_log_data(USBRedirDevice *dev, const char *desc, + const uint8_t *data, int len) +{ + int i, j, n; + + if (dev->debug < usbredirparser_debug_data) { + return; + } + + for (i = 0; i < len; i += j) { + char buf[128]; + + n = sprintf(buf, "%s", desc); + for (j = 0; j < 8 && i + j < len; j++) { + n += sprintf(buf + n, " %02X", data[i + j]); + } + error_report("%s", buf); + } +} + +/* + * usbredirparser io functions + */ + +static int usbredir_read(void *priv, uint8_t *data, int count) +{ + USBRedirDevice *dev = priv; + + if (dev->read_buf_size < count) { + count = dev->read_buf_size; + } + + memcpy(data, dev->read_buf, count); + + dev->read_buf_size -= count; + if (dev->read_buf_size) { + dev->read_buf += count; + } else { + dev->read_buf = NULL; + } + + return count; +} + +static int usbredir_write(void *priv, uint8_t *data, int count) +{ + USBRedirDevice *dev = priv; + + if (!dev->cs->opened) { + return 0; + } + + return qemu_chr_fe_write(dev->cs, data, count); +} + +/* + * Async and buffered packets helpers + */ + +static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) +{ + AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB)); + aurb->dev = dev; + aurb->packet = p; + aurb->packet_id = dev->packet_id; + QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); + dev->packet_id++; + + return aurb; +} + +static void async_free(USBRedirDevice *dev, AsyncURB *aurb) +{ + QTAILQ_REMOVE(&dev->asyncq, aurb, next); + g_free(aurb); +} + +static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id) +{ + AsyncURB *aurb; + + QTAILQ_FOREACH(aurb, &dev->asyncq, next) { + if (aurb->packet_id == packet_id) { + return aurb; + } + } + ERROR("could not find async urb for packet_id %u\n", packet_id); + return NULL; +} + +static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + AsyncURB *aurb; + + QTAILQ_FOREACH(aurb, &dev->asyncq, next) { + if (p != aurb->packet) { + continue; + } + + DPRINTF("async cancel id %u\n", aurb->packet_id); + usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id); + usbredirparser_do_write(dev->parser); + + /* Mark it as dead */ + aurb->packet = NULL; + break; + } +} + +static void bufp_alloc(USBRedirDevice *dev, + uint8_t *data, int len, int status, uint8_t ep) +{ + struct buf_packet *bufp; + + if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets && + dev->endpoint[EP2I(ep)].bufpq_size > + 2 * dev->endpoint[EP2I(ep)].bufpq_target_size) { + DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep); + dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1; + } + /* Since we're interupting the stream anyways, drop enough packets to get + back to our target buffer size */ + if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) { + if (dev->endpoint[EP2I(ep)].bufpq_size > + dev->endpoint[EP2I(ep)].bufpq_target_size) { + free(data); + return; + } + dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; + } + + bufp = g_malloc(sizeof(struct buf_packet)); + bufp->data = data; + bufp->len = len; + bufp->status = status; + QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); + dev->endpoint[EP2I(ep)].bufpq_size++; +} + +static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, + uint8_t ep) +{ + QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); + dev->endpoint[EP2I(ep)].bufpq_size--; + free(bufp->data); + g_free(bufp); +} + +static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep) +{ + struct buf_packet *buf, *buf_next; + + QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) { + bufp_free(dev, buf, ep); + } +} + +/* + * USBDevice callbacks + */ + +static void usbredir_handle_reset(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + DPRINTF("reset device\n"); + usbredirparser_send_reset(dev->parser); + usbredirparser_do_write(dev->parser); +} + +static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + uint8_t ep) +{ + int status, len; + if (!dev->endpoint[EP2I(ep)].iso_started && + !dev->endpoint[EP2I(ep)].iso_error) { + struct usb_redir_start_iso_stream_header start_iso = { + .endpoint = ep, + }; + int pkts_per_sec; + + if (dev->dev.speed == USB_SPEED_HIGH) { + pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval; + } else { + pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval; + } + /* Testing has shown that we need circa 60 ms buffer */ + dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000; + + /* Aim for approx 100 interrupts / second on the client to + balance latency and interrupt load */ + start_iso.pkts_per_urb = pkts_per_sec / 100; + if (start_iso.pkts_per_urb < 1) { + start_iso.pkts_per_urb = 1; + } else if (start_iso.pkts_per_urb > 32) { + start_iso.pkts_per_urb = 32; + } + + start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size + + start_iso.pkts_per_urb - 1) / + start_iso.pkts_per_urb; + /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest + as overflow buffer. Also see the usbredir protocol documentation */ + if (!(ep & USB_DIR_IN)) { + start_iso.no_urbs *= 2; + } + if (start_iso.no_urbs > 16) { + start_iso.no_urbs = 16; + } + + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso); + usbredirparser_do_write(dev->parser); + DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n", + pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep); + dev->endpoint[EP2I(ep)].iso_started = 1; + dev->endpoint[EP2I(ep)].bufpq_prefilled = 0; + dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; + } + + if (ep & USB_DIR_IN) { + struct buf_packet *isop; + + if (dev->endpoint[EP2I(ep)].iso_started && + !dev->endpoint[EP2I(ep)].bufpq_prefilled) { + if (dev->endpoint[EP2I(ep)].bufpq_size < + dev->endpoint[EP2I(ep)].bufpq_target_size) { + return usbredir_handle_status(dev, 0, 0); + } + dev->endpoint[EP2I(ep)].bufpq_prefilled = 1; + } + + isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); + if (isop == NULL) { + DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n", + ep, dev->endpoint[EP2I(ep)].iso_error); + /* Re-fill the buffer */ + dev->endpoint[EP2I(ep)].bufpq_prefilled = 0; + /* Check iso_error for stream errors, otherwise its an underrun */ + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; + return status ? USB_RET_IOERROR : 0; + } + DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep, + isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size); + + status = isop->status; + if (status != usb_redir_success) { + bufp_free(dev, isop, ep); + return USB_RET_IOERROR; + } + + len = isop->len; + if (len > p->iov.size) { + ERROR("received iso data is larger then packet ep %02X (%d > %d)\n", + ep, len, (int)p->iov.size); + bufp_free(dev, isop, ep); + return USB_RET_BABBLE; + } + usb_packet_copy(p, isop->data, len); + bufp_free(dev, isop, ep); + return len; + } else { + /* If the stream was not started because of a pending error don't + send the packet to the usb-host */ + if (dev->endpoint[EP2I(ep)].iso_started) { + struct usb_redir_iso_packet_header iso_packet = { + .endpoint = ep, + .length = p->iov.size + }; + uint8_t buf[p->iov.size]; + /* No id, we look at the ep when receiving a status back */ + usb_packet_copy(p, buf, p->iov.size); + usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, + buf, p->iov.size); + usbredirparser_do_write(dev->parser); + } + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; + DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status, + p->iov.size); + return usbredir_handle_status(dev, status, p->iov.size); + } +} + +static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) +{ + struct usb_redir_stop_iso_stream_header stop_iso_stream = { + .endpoint = ep + }; + if (dev->endpoint[EP2I(ep)].iso_started) { + usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream); + DPRINTF("iso stream stopped ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 0; + } + dev->endpoint[EP2I(ep)].iso_error = 0; + usbredir_free_bufpq(dev, ep); +} + +static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, + uint8_t ep) +{ + AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_bulk_packet_header bulk_packet; + + DPRINTF("bulk-out ep %02X len %zd id %u\n", ep, + p->iov.size, aurb->packet_id); + + bulk_packet.endpoint = ep; + bulk_packet.length = p->iov.size; + bulk_packet.stream_id = 0; + aurb->bulk_packet = bulk_packet; + + if (ep & USB_DIR_IN) { + usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + &bulk_packet, NULL, 0); + } else { + uint8_t buf[p->iov.size]; + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "bulk data out:", buf, p->iov.size); + usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + &bulk_packet, buf, p->iov.size); + } + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + USBPacket *p, uint8_t ep) +{ + if (ep & USB_DIR_IN) { + /* Input interrupt endpoint, buffered packet input */ + struct buf_packet *intp; + int status, len; + + if (!dev->endpoint[EP2I(ep)].interrupt_started && + !dev->endpoint[EP2I(ep)].interrupt_error) { + struct usb_redir_start_interrupt_receiving_header start_int = { + .endpoint = ep, + }; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_start_interrupt_receiving(dev->parser, 0, + &start_int); + usbredirparser_do_write(dev->parser); + DPRINTF("interrupt recv started ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 1; + /* We don't really want to drop interrupt packets ever, but + having some upper limit to how much we buffer is good. */ + dev->endpoint[EP2I(ep)].bufpq_target_size = 1000; + dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; + } + + intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); + if (intp == NULL) { + DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep); + /* Check interrupt_error for stream errors */ + status = dev->endpoint[EP2I(ep)].interrupt_error; + dev->endpoint[EP2I(ep)].interrupt_error = 0; + if (status) { + return usbredir_handle_status(dev, status, 0); + } + return USB_RET_NAK; + } + DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, + intp->status, intp->len); + + status = intp->status; + if (status != usb_redir_success) { + bufp_free(dev, intp, ep); + return usbredir_handle_status(dev, status, 0); + } + + len = intp->len; + if (len > p->iov.size) { + ERROR("received int data is larger then packet ep %02X\n", ep); + bufp_free(dev, intp, ep); + return USB_RET_BABBLE; + } + usb_packet_copy(p, intp->data, len); + bufp_free(dev, intp, ep); + return len; + } else { + /* Output interrupt endpoint, normal async operation */ + AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_interrupt_packet_header interrupt_packet; + uint8_t buf[p->iov.size]; + + DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size, + aurb->packet_id); + + interrupt_packet.endpoint = ep; + interrupt_packet.length = p->iov.size; + aurb->interrupt_packet = interrupt_packet; + + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); + usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, + &interrupt_packet, buf, p->iov.size); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; + } +} + +static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev, + uint8_t ep) +{ + struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = { + .endpoint = ep + }; + if (dev->endpoint[EP2I(ep)].interrupt_started) { + usbredirparser_send_stop_interrupt_receiving(dev->parser, 0, + &stop_interrupt_recv); + DPRINTF("interrupt recv stopped ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 0; + } + dev->endpoint[EP2I(ep)].interrupt_error = 0; + usbredir_free_bufpq(dev, ep); +} + +static int usbredir_handle_data(USBDevice *udev, USBPacket *p) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + uint8_t ep; + + ep = p->ep->nr; + if (p->pid == USB_TOKEN_IN) { + ep |= USB_DIR_IN; + } + + switch (dev->endpoint[EP2I(ep)].type) { + case USB_ENDPOINT_XFER_CONTROL: + ERROR("handle_data called for control transfer on ep %02X\n", ep); + return USB_RET_NAK; + case USB_ENDPOINT_XFER_ISOC: + return usbredir_handle_iso_data(dev, p, ep); + case USB_ENDPOINT_XFER_BULK: + return usbredir_handle_bulk_data(dev, p, ep); + case USB_ENDPOINT_XFER_INT: + return usbredir_handle_interrupt_data(dev, p, ep); + default: + ERROR("handle_data ep %02X has unknown type %d\n", ep, + dev->endpoint[EP2I(ep)].type); + return USB_RET_NAK; + } +} + +static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, + int config) +{ + struct usb_redir_set_configuration_header set_config; + AsyncURB *aurb = async_alloc(dev, p); + int i; + + DPRINTF("set config %d id %u\n", config, aurb->packet_id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + switch (dev->endpoint[i].type) { + case USB_ENDPOINT_XFER_ISOC: + usbredir_stop_iso_stream(dev, I2EP(i)); + break; + case USB_ENDPOINT_XFER_INT: + if (i & 0x10) { + usbredir_stop_interrupt_receiving(dev, I2EP(i)); + } + break; + } + usbredir_free_bufpq(dev, I2EP(i)); + } + + set_config.configuration = config; + usbredirparser_send_set_configuration(dev->parser, aurb->packet_id, + &set_config); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) +{ + AsyncURB *aurb = async_alloc(dev, p); + + DPRINTF("get config id %u\n", aurb->packet_id); + + aurb->get = 1; + usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, + int interface, int alt) +{ + struct usb_redir_set_alt_setting_header set_alt; + AsyncURB *aurb = async_alloc(dev, p); + int i; + + DPRINTF("set interface %d alt %d id %u\n", interface, alt, + aurb->packet_id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + if (dev->endpoint[i].interface == interface) { + switch (dev->endpoint[i].type) { + case USB_ENDPOINT_XFER_ISOC: + usbredir_stop_iso_stream(dev, I2EP(i)); + break; + case USB_ENDPOINT_XFER_INT: + if (i & 0x10) { + usbredir_stop_interrupt_receiving(dev, I2EP(i)); + } + break; + } + usbredir_free_bufpq(dev, I2EP(i)); + } + } + + set_alt.interface = interface; + set_alt.alt = alt; + usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id, + &set_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, + int interface) +{ + struct usb_redir_get_alt_setting_header get_alt; + AsyncURB *aurb = async_alloc(dev, p); + + DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); + + get_alt.interface = interface; + aurb->get = 1; + usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, + &get_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + struct usb_redir_control_packet_header control_packet; + AsyncURB *aurb; + + /* Special cases for certain standard device requests */ + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + DPRINTF("set address %d\n", value); + dev->dev.addr = value; + return 0; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + return usbredir_set_config(dev, p, value & 0xff); + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + return usbredir_get_config(dev, p); + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + return usbredir_set_interface(dev, p, index, value); + case InterfaceRequest | USB_REQ_GET_INTERFACE: + return usbredir_get_interface(dev, p, index); + } + + /* "Normal" ctrl requests */ + aurb = async_alloc(dev, p); + + /* Note request is (bRequestType << 8) | bRequest */ + DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n", + request >> 8, request & 0xff, value, index, length, + aurb->packet_id); + + control_packet.request = request & 0xFF; + control_packet.requesttype = request >> 8; + control_packet.endpoint = control_packet.requesttype & USB_DIR_IN; + control_packet.value = value; + control_packet.index = index; + control_packet.length = length; + aurb->control_packet = control_packet; + + if (control_packet.requesttype & USB_DIR_IN) { + usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + &control_packet, NULL, 0); + } else { + usbredir_log_data(dev, "ctrl data out:", data, length); + usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + &control_packet, data, length); + } + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +/* + * Close events can be triggered by usbredirparser_do_write which gets called + * from within the USBDevice data / control packet callbacks and doing a + * usb_detach from within these callbacks is not a good idea. + * + * So we use a bh handler to take care of close events. We also handle + * open events from this callback to make sure that a close directly followed + * by an open gets handled in the right order. + */ +static void usbredir_open_close_bh(void *opaque) +{ + USBRedirDevice *dev = opaque; + uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; + + usbredir_device_disconnect(dev); + + if (dev->parser) { + usbredirparser_destroy(dev->parser); + dev->parser = NULL; + } + + if (dev->cs->opened) { + dev->parser = qemu_oom_check(usbredirparser_create()); + dev->parser->priv = dev; + dev->parser->log_func = usbredir_log; + dev->parser->read_func = usbredir_read; + dev->parser->write_func = usbredir_write; + dev->parser->hello_func = usbredir_hello; + dev->parser->device_connect_func = usbredir_device_connect; + dev->parser->device_disconnect_func = usbredir_device_disconnect; + dev->parser->interface_info_func = usbredir_interface_info; + dev->parser->ep_info_func = usbredir_ep_info; + dev->parser->configuration_status_func = usbredir_configuration_status; + dev->parser->alt_setting_status_func = usbredir_alt_setting_status; + dev->parser->iso_stream_status_func = usbredir_iso_stream_status; + dev->parser->interrupt_receiving_status_func = + usbredir_interrupt_receiving_status; + dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; + dev->parser->control_packet_func = usbredir_control_packet; + dev->parser->bulk_packet_func = usbredir_bulk_packet; + dev->parser->iso_packet_func = usbredir_iso_packet; + dev->parser->interrupt_packet_func = usbredir_interrupt_packet; + dev->read_buf = NULL; + dev->read_buf_size = 0; + + usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); + usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); + usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0); + usbredirparser_do_write(dev->parser); + } +} + +static void usbredir_do_attach(void *opaque) +{ + USBRedirDevice *dev = opaque; + + usb_device_attach(&dev->dev); +} + +/* + * chardev callbacks + */ + +static int usbredir_chardev_can_read(void *opaque) +{ + USBRedirDevice *dev = opaque; + + if (dev->parser) { + /* usbredir_parser_do_read will consume *all* data we give it */ + return 1024 * 1024; + } else { + /* usbredir_open_close_bh hasn't handled the open event yet */ + return 0; + } +} + +static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) +{ + USBRedirDevice *dev = opaque; + + /* No recursion allowed! */ + assert(dev->read_buf == NULL); + + dev->read_buf = buf; + dev->read_buf_size = size; + + usbredirparser_do_read(dev->parser); + /* Send any acks, etc. which may be queued now */ + usbredirparser_do_write(dev->parser); +} + +static void usbredir_chardev_event(void *opaque, int event) +{ + USBRedirDevice *dev = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + case CHR_EVENT_CLOSED: + qemu_bh_schedule(dev->open_close_bh); + break; + } +} + +/* + * init + destroy + */ + +static int usbredir_initfn(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + int i; + + if (dev->cs == NULL) { + qerror_report(QERR_MISSING_PARAMETER, "chardev"); + return -1; + } + + if (dev->filter_str) { + i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|", + &dev->filter_rules, + &dev->filter_rules_count); + if (i) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "filter", + "a usb device filter string"); + return -1; + } + } + + dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + + QTAILQ_INIT(&dev->asyncq); + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } + + /* We'll do the attach once we receive the speed from the usb-host */ + udev->auto_attach = 0; + + /* Let the backend know we are ready */ + qemu_chr_fe_open(dev->cs); + qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, + usbredir_chardev_read, usbredir_chardev_event, dev); + + return 0; +} + +static void usbredir_cleanup_device_queues(USBRedirDevice *dev) +{ + AsyncURB *aurb, *next_aurb; + int i; + + QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) { + async_free(dev, aurb); + } + for (i = 0; i < MAX_ENDPOINTS; i++) { + usbredir_free_bufpq(dev, I2EP(i)); + } +} + +static void usbredir_handle_destroy(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + qemu_chr_fe_close(dev->cs); + qemu_chr_delete(dev->cs); + /* Note must be done after qemu_chr_close, as that causes a close event */ + qemu_bh_delete(dev->open_close_bh); + + qemu_del_timer(dev->attach_timer); + qemu_free_timer(dev->attach_timer); + + usbredir_cleanup_device_queues(dev); + + if (dev->parser) { + usbredirparser_destroy(dev->parser); + } + + free(dev->filter_rules); +} + +static int usbredir_check_filter(USBRedirDevice *dev) +{ + if (dev->interface_info.interface_count == 0) { + ERROR("No interface info for device\n"); + goto error; + } + + if (dev->filter_rules) { + if (!usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_connect_device_version)) { + ERROR("Device filter specified and peer does not have the " + "connect_device_version capability\n"); + goto error; + } + + if (usbredirfilter_check( + dev->filter_rules, + dev->filter_rules_count, + dev->device_info.device_class, + dev->device_info.device_subclass, + dev->device_info.device_protocol, + dev->interface_info.interface_class, + dev->interface_info.interface_subclass, + dev->interface_info.interface_protocol, + dev->interface_info.interface_count, + dev->device_info.vendor_id, + dev->device_info.product_id, + dev->device_info.device_version_bcd, + 0) != 0) { + goto error; + } + } + + return 0; + +error: + usbredir_device_disconnect(dev); + if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { + usbredirparser_send_filter_reject(dev->parser); + usbredirparser_do_write(dev->parser); + } + return -1; +} + +/* + * usbredirparser packet complete callbacks + */ + +static int usbredir_handle_status(USBRedirDevice *dev, + int status, int actual_len) +{ + switch (status) { + case usb_redir_success: + return actual_len; + case usb_redir_stall: + return USB_RET_STALL; + case usb_redir_cancelled: + WARNING("returning cancelled packet to HC?\n"); + return USB_RET_NAK; + case usb_redir_inval: + WARNING("got invalid param error from usb-host?\n"); + return USB_RET_NAK; + case usb_redir_ioerror: + case usb_redir_timeout: + default: + return USB_RET_IOERROR; + } +} + +static void usbredir_hello(void *priv, struct usb_redir_hello_header *h) +{ + USBRedirDevice *dev = priv; + + /* Try to send the filter info now that we've the usb-host's caps */ + if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) && + dev->filter_rules) { + usbredirparser_send_filter_filter(dev->parser, dev->filter_rules, + dev->filter_rules_count); + usbredirparser_do_write(dev->parser); + } +} + +static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect) +{ + USBRedirDevice *dev = priv; + const char *speed; + + if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) { + ERROR("Received device connect while already connected\n"); + return; + } + + switch (device_connect->speed) { + case usb_redir_speed_low: + speed = "low speed"; + dev->dev.speed = USB_SPEED_LOW; + break; + case usb_redir_speed_full: + speed = "full speed"; + dev->dev.speed = USB_SPEED_FULL; + break; + case usb_redir_speed_high: + speed = "high speed"; + dev->dev.speed = USB_SPEED_HIGH; + break; + case usb_redir_speed_super: + speed = "super speed"; + dev->dev.speed = USB_SPEED_SUPER; + break; + default: + speed = "unknown speed"; + dev->dev.speed = USB_SPEED_FULL; + } + + if (usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_connect_device_version)) { + INFO("attaching %s device %04x:%04x version %d.%d class %02x\n", + speed, device_connect->vendor_id, device_connect->product_id, + ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 + + ((device_connect->device_version_bcd & 0x0f00) >> 8), + ((device_connect->device_version_bcd & 0x00f0) >> 4) * 10 + + ((device_connect->device_version_bcd & 0x000f) >> 0), + device_connect->device_class); + } else { + INFO("attaching %s device %04x:%04x class %02x\n", speed, + device_connect->vendor_id, device_connect->product_id, + device_connect->device_class); + } + + dev->dev.speedmask = (1 << dev->dev.speed); + dev->device_info = *device_connect; + + if (usbredir_check_filter(dev)) { + WARNING("Device %04x:%04x rejected by device filter, not attaching\n", + device_connect->vendor_id, device_connect->product_id); + return; + } + + qemu_mod_timer(dev->attach_timer, dev->next_attach_time); +} + +static void usbredir_device_disconnect(void *priv) +{ + USBRedirDevice *dev = priv; + int i; + + /* Stop any pending attaches */ + qemu_del_timer(dev->attach_timer); + + if (dev->dev.attached) { + usb_device_detach(&dev->dev); + /* + * Delay next usb device attach to give the guest a chance to see + * see the detach / attach in case of quick close / open succession + */ + dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200; + } + + /* Reset state so that the next dev connected starts with a clean slate */ + usbredir_cleanup_device_queues(dev); + memset(dev->endpoint, 0, sizeof(dev->endpoint)); + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } + usb_ep_init(&dev->dev); + dev->interface_info.interface_count = 0; +} + +static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info) +{ + USBRedirDevice *dev = priv; + + dev->interface_info = *interface_info; + + /* + * If we receive interface info after the device has already been + * connected (ie on a set_config), re-check the filter. + */ + if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) { + if (usbredir_check_filter(dev)) { + ERROR("Device no longer matches filter after interface info " + "change, disconnecting!\n"); + } + } +} + +static void usbredir_ep_info(void *priv, + struct usb_redir_ep_info_header *ep_info) +{ + USBRedirDevice *dev = priv; + struct USBEndpoint *usb_ep; + int i; + + for (i = 0; i < MAX_ENDPOINTS; i++) { + dev->endpoint[i].type = ep_info->type[i]; + dev->endpoint[i].interval = ep_info->interval[i]; + dev->endpoint[i].interface = ep_info->interface[i]; + switch (dev->endpoint[i].type) { + case usb_redir_type_invalid: + break; + case usb_redir_type_iso: + case usb_redir_type_interrupt: + if (dev->endpoint[i].interval == 0) { + ERROR("Received 0 interval for isoc or irq endpoint\n"); + usbredir_device_disconnect(dev); + } + /* Fall through */ + case usb_redir_type_control: + case usb_redir_type_bulk: + DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i), + dev->endpoint[i].type, dev->endpoint[i].interface); + break; + default: + ERROR("Received invalid endpoint type\n"); + usbredir_device_disconnect(dev); + return; + } + usb_ep = usb_ep_get(&dev->dev, + (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, + i & 0x0f); + usb_ep->type = dev->endpoint[i].type; + usb_ep->ifnum = dev->endpoint[i].interface; + } +} + +static void usbredir_configuration_status(void *priv, uint32_t id, + struct usb_redir_configuration_status_header *config_status) +{ + USBRedirDevice *dev = priv; + AsyncURB *aurb; + int len = 0; + + DPRINTF("set config status %d config %d id %u\n", config_status->status, + config_status->configuration, id); + + aurb = async_find(dev, id); + if (!aurb) { + return; + } + if (aurb->packet) { + if (aurb->get) { + dev->dev.data_buf[0] = config_status->configuration; + len = 1; + } + aurb->packet->result = + usbredir_handle_status(dev, config_status->status, len); + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); +} + +static void usbredir_alt_setting_status(void *priv, uint32_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status) +{ + USBRedirDevice *dev = priv; + AsyncURB *aurb; + int len = 0; + + DPRINTF("alt status %d intf %d alt %d id: %u\n", + alt_setting_status->status, + alt_setting_status->interface, + alt_setting_status->alt, id); + + aurb = async_find(dev, id); + if (!aurb) { + return; + } + if (aurb->packet) { + if (aurb->get) { + dev->dev.data_buf[0] = alt_setting_status->alt; + len = 1; + } + aurb->packet->result = + usbredir_handle_status(dev, alt_setting_status->status, len); + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); +} + +static void usbredir_iso_stream_status(void *priv, uint32_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status) +{ + USBRedirDevice *dev = priv; + uint8_t ep = iso_stream_status->endpoint; + + DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status, + ep, id); + + if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) { + return; + } + + dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status; + if (iso_stream_status->status == usb_redir_stall) { + DPRINTF("iso stream stopped by peer ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 0; + } +} + +static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status) +{ + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_receiving_status->endpoint; + + DPRINTF("interrupt recv status %d ep %02X id %u\n", + interrupt_receiving_status->status, ep, id); + + if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) { + return; + } + + dev->endpoint[EP2I(ep)].interrupt_error = + interrupt_receiving_status->status; + if (interrupt_receiving_status->status == usb_redir_stall) { + DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 0; + } +} + +static void usbredir_bulk_streams_status(void *priv, uint32_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status) +{ +} + +static void usbredir_control_packet(void *priv, uint32_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + int len = control_packet->length; + AsyncURB *aurb; + + DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, + len, id); + + aurb = async_find(dev, id); + if (!aurb) { + free(data); + return; + } + + aurb->control_packet.status = control_packet->status; + aurb->control_packet.length = control_packet->length; + if (memcmp(&aurb->control_packet, control_packet, + sizeof(*control_packet))) { + ERROR("return control packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + len = usbredir_handle_status(dev, control_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "ctrl data in:", data, data_len); + if (data_len <= sizeof(dev->dev.data_buf)) { + memcpy(dev->dev.data_buf, data, data_len); + } else { + ERROR("ctrl buffer too small (%d > %zu)\n", + data_len, sizeof(dev->dev.data_buf)); + len = USB_RET_STALL; + } + } + aurb->packet->result = len; + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + free(data); +} + +static void usbredir_bulk_packet(void *priv, uint32_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = bulk_packet->endpoint; + int len = bulk_packet->length; + AsyncURB *aurb; + + DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, + ep, len, id); + + aurb = async_find(dev, id); + if (!aurb) { + free(data); + return; + } + + if (aurb->bulk_packet.endpoint != bulk_packet->endpoint || + aurb->bulk_packet.stream_id != bulk_packet->stream_id) { + ERROR("return bulk packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + len = usbredir_handle_status(dev, bulk_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "bulk data in:", data, data_len); + if (data_len <= aurb->packet->iov.size) { + usb_packet_copy(aurb->packet, data, data_len); + } else { + ERROR("bulk buffer too small (%d > %zd)\n", data_len, + aurb->packet->iov.size); + len = USB_RET_STALL; + } + } + aurb->packet->result = len; + usb_packet_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + free(data); +} + +static void usbredir_iso_packet(void *priv, uint32_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = iso_packet->endpoint; + + DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep, + data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { + ERROR("received iso packet for non iso endpoint %02X\n", ep); + free(data); + return; + } + + if (dev->endpoint[EP2I(ep)].iso_started == 0) { + DPRINTF("received iso packet for non started stream ep %02X\n", ep); + free(data); + return; + } + + /* bufp_alloc also adds the packet to the ep queue */ + bufp_alloc(dev, data, data_len, iso_packet->status, ep); +} + +static void usbredir_interrupt_packet(void *priv, uint32_t id, + struct usb_redir_interrupt_packet_header *interrupt_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_packet->endpoint; + + DPRINTF("interrupt-in status %d ep %02X len %d id %u\n", + interrupt_packet->status, ep, data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { + ERROR("received int packet for non interrupt endpoint %02X\n", ep); + free(data); + return; + } + + if (ep & USB_DIR_IN) { + if (dev->endpoint[EP2I(ep)].interrupt_started == 0) { + DPRINTF("received int packet while not started ep %02X\n", ep); + free(data); + return; + } + + /* bufp_alloc also adds the packet to the ep queue */ + bufp_alloc(dev, data, data_len, interrupt_packet->status, ep); + } else { + int len = interrupt_packet->length; + + AsyncURB *aurb = async_find(dev, id); + if (!aurb) { + return; + } + + if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) { + ERROR("return int packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + aurb->packet->result = usbredir_handle_status(dev, + interrupt_packet->status, len); + usb_packet_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + } +} + +static Property usbredir_properties[] = { + DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), + DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), + DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), + DEFINE_PROP_END_OF_LIST(), +}; + +static void usbredir_class_initfn(ObjectClass *klass, void *data) +{ + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + uc->init = usbredir_initfn; + uc->product_desc = "USB Redirection Device"; + uc->handle_destroy = usbredir_handle_destroy; + uc->cancel_packet = usbredir_cancel_packet; + uc->handle_reset = usbredir_handle_reset; + uc->handle_data = usbredir_handle_data; + uc->handle_control = usbredir_handle_control; + dc->props = usbredir_properties; +} + +static TypeInfo usbredir_dev_info = { + .name = "usb-redir", + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(USBRedirDevice), + .class_init = usbredir_class_initfn, +}; + +static void usbredir_register_types(void) +{ + type_register_static(&usbredir_dev_info); +} + +type_init(usbredir_register_types) diff --git a/hw/versatilepb.c b/hw/versatilepb.c index b9102f4a54..25afb1eb31 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -13,7 +13,6 @@ #include "net.h" #include "sysemu.h" #include "pci.h" -#include "usb-ohci.h" #include "boards.h" #include "blockdev.h" #include "exec-memory.h" @@ -168,7 +167,7 @@ static void versatile_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model, int board_id) { - CPUState *env; + CPUARMState *env; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); qemu_irq *cpu_pic; @@ -240,7 +239,7 @@ static void versatile_init(ram_addr_t ram_size, } } if (usb_enabled) { - usb_ohci_init_pci(pci_bus, -1); + pci_create_simple(pci_bus, -1, "pci-ohci"); } n = drive_get_max_bus(IF_SCSI); while (n >= 0) { diff --git a/hw/vexpress.c b/hw/vexpress.c index b9aafec4cc..18d87ac378 100644 --- a/hw/vexpress.c +++ b/hw/vexpress.c @@ -159,7 +159,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, const char *cpu_model, qemu_irq *pic, uint32_t *proc_id) { - CPUState *env = NULL; + CPUARMState *env = NULL; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *lowram = g_new(MemoryRegion, 1); @@ -259,7 +259,7 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, qemu_irq *pic, uint32_t *proc_id) { int n; - CPUState *env = NULL; + CPUARMState *env = NULL; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); diff --git a/hw/vga.c b/hw/vga.c index 5994f43b75..6dc98f6c2a 100644 --- a/hw/vga.c +++ b/hw/vga.c @@ -30,6 +30,7 @@ #include "pixel_ops.h" #include "qemu-timer.h" #include "xen.h" +#include "trace.h" //#define DEBUG_VGA //#define DEBUG_VGA_MEM @@ -2372,6 +2373,7 @@ int ppm_save(const char *filename, struct DisplaySurface *ds) int ret; char *linebuf, *pbuf; + trace_ppm_save(filename, ds); f = fopen(filename, "wb"); if (!f) return -1; diff --git a/hw/vhost.c b/hw/vhost.c index 5ece659f4a..8d3ba5b608 100644 --- a/hw/vhost.c +++ b/hw/vhost.c @@ -436,6 +436,14 @@ static bool vhost_section(MemoryRegionSection *section) && memory_region_is_ram(section->mr); } +static void vhost_begin(MemoryListener *listener) +{ +} + +static void vhost_commit(MemoryListener *listener) +{ +} + static void vhost_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -476,6 +484,11 @@ static void vhost_region_del(MemoryListener *listener, } } +static void vhost_region_nop(MemoryListener *listener, + MemoryRegionSection *section) +{ +} + static int vhost_virtqueue_set_addr(struct vhost_dev *dev, struct vhost_virtqueue *vq, unsigned idx, bool enable_log) @@ -720,6 +733,18 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev, 0, virtio_queue_get_desc_size(vdev, idx)); } +static void vhost_eventfd_add(MemoryListener *listener, + MemoryRegionSection *section, + bool match_data, uint64_t data, int fd) +{ +} + +static void vhost_eventfd_del(MemoryListener *listener, + MemoryRegionSection *section, + bool match_data, uint64_t data, int fd) +{ +} + int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force) { uint64_t features; @@ -744,13 +769,19 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force) hdev->features = features; hdev->memory_listener = (MemoryListener) { + .begin = vhost_begin, + .commit = vhost_commit, .region_add = vhost_region_add, .region_del = vhost_region_del, + .region_nop = vhost_region_nop, .log_start = vhost_log_start, .log_stop = vhost_log_stop, .log_sync = vhost_log_sync, .log_global_start = vhost_log_global_start, .log_global_stop = vhost_log_global_stop, + .eventfd_add = vhost_eventfd_add, + .eventfd_del = vhost_eventfd_del, + .priority = 10 }; hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions)); hdev->n_mem_sections = 0; @@ -759,7 +790,7 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force) hdev->log_size = 0; hdev->log_enabled = false; hdev->started = false; - memory_listener_register(&hdev->memory_listener); + memory_listener_register(&hdev->memory_listener, NULL); hdev->force = force; return 0; fail: diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c index f8d2b1be04..4a133b5d1e 100644 --- a/hw/virtex_ml507.c +++ b/hw/virtex_ml507.c @@ -56,7 +56,7 @@ static struct boot_info } boot_info; /* Create reset TLB entries for BookE, spanning the 32bit addr space. */ -static void mmubooke_create_initial_mapping(CPUState *env, +static void mmubooke_create_initial_mapping(CPUPPCState *env, target_ulong va, target_phys_addr_t pa) { @@ -78,12 +78,12 @@ static void mmubooke_create_initial_mapping(CPUState *env, tlb->PID = 0; } -static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, +static CPUPPCState *ppc440_init_xilinx(ram_addr_t *ram_size, int do_init, const char *cpu_model, uint32_t sysclk) { - CPUState *env; + CPUPPCState *env; qemu_irq *irqs; env = cpu_init(cpu_model); @@ -106,10 +106,10 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; + CPUPPCState *env = opaque; struct boot_info *bi = env->load_info; - cpu_reset(env); + cpu_state_reset(env); /* Linux Kernel Parameters (passing device tree): * r3: pointer to the fdt * r4: 0 @@ -188,7 +188,7 @@ static void virtex_init(ram_addr_t ram_size, { MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev; - CPUState *env; + CPUPPCState *env; target_phys_addr_t ram_base = 0; DriveInfo *dinfo; MemoryRegion *phys_ram = g_new(MemoryRegion, 1); diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index e607edc915..45d54faeb5 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -613,5 +613,7 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) void virtio_scsi_exit(VirtIODevice *vdev) { + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + unregister_savevm(s->qdev, "virtio-scsi", s); virtio_cleanup(vdev); } diff --git a/hw/vmport.c b/hw/vmport.c index 9373be9775..a4f52ee5bb 100644 --- a/hw/vmport.c +++ b/hw/vmport.c @@ -57,7 +57,7 @@ void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque) static uint32_t vmport_ioport_read(void *opaque, uint32_t addr) { VMPortState *s = opaque; - CPUState *env = cpu_single_env; + CPUX86State *env = cpu_single_env; unsigned char command; uint32_t eax; @@ -83,21 +83,21 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr) static void vmport_ioport_write(void *opaque, uint32_t addr, uint32_t val) { - CPUState *env = cpu_single_env; + CPUX86State *env = cpu_single_env; env->regs[R_EAX] = vmport_ioport_read(opaque, addr); } static uint32_t vmport_cmd_get_version(void *opaque, uint32_t addr) { - CPUState *env = cpu_single_env; + CPUX86State *env = cpu_single_env; env->regs[R_EBX] = VMPORT_MAGIC; return 6; } static uint32_t vmport_cmd_ram_size(void *opaque, uint32_t addr) { - CPUState *env = cpu_single_env; + CPUX86State *env = cpu_single_env; env->regs[R_EBX] = 0x1177; return ram_size; } @@ -105,7 +105,7 @@ static uint32_t vmport_cmd_ram_size(void *opaque, uint32_t addr) /* vmmouse helpers */ void vmmouse_get_data(uint32_t *data) { - CPUState *env = cpu_single_env; + CPUX86State *env = cpu_single_env; data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX]; data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX]; @@ -114,7 +114,7 @@ void vmmouse_get_data(uint32_t *data) void vmmouse_set_data(const uint32_t *data) { - CPUState *env = cpu_single_env; + CPUX86State *env = cpu_single_env; env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1]; env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3]; diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 7985d11d5a..7eee770eea 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -36,7 +36,7 @@ static void xen_init_pv(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - CPUState *env; + CPUX86State *env; DriveInfo *dinfo; int i; diff --git a/hw/xics.c b/hw/xics.c index 1c5eaa4135..668a0d6484 100644 --- a/hw/xics.c +++ b/hw/xics.c @@ -132,9 +132,9 @@ static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr) { struct icp_server_state *ss = icp->ss + server; - ics_eoi(icp->ics, xirr & XISR_MASK); /* Send EOI -> ICS */ ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); + ics_eoi(icp->ics, xirr & XISR_MASK); if (!XISR(ss)) { icp_resend(icp, server); } @@ -165,8 +165,9 @@ struct ics_irq_state { int server; uint8_t priority; uint8_t saved_priority; - /* int pending:1; */ - /* int presented:1; */ + enum xics_irq_type type; + int asserted:1; + int sent:1; int rejected:1; int masked_pending:1; }; @@ -185,9 +186,32 @@ static int ics_valid_irq(struct ics_state *ics, uint32_t nr) && (nr < (ics->offset + ics->nr_irqs)); } -static void ics_set_irq_msi(void *opaque, int srcno, int val) +static void resend_msi(struct ics_state *ics, int srcno) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + /* FIXME: filter by server#? */ + if (irq->rejected) { + irq->rejected = 0; + if (irq->priority != 0xff) { + icp_irq(ics->icp, irq->server, srcno + ics->offset, + irq->priority); + } + } +} + +static void resend_lsi(struct ics_state *ics, int srcno) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + if ((irq->priority != 0xff) && irq->asserted && !irq->sent) { + irq->sent = 1; + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); + } +} + +static void set_irq_msi(struct ics_state *ics, int srcno, int val) { - struct ics_state *ics = (struct ics_state *)opaque; struct ics_irq_state *irq = ics->irqs + srcno; if (val) { @@ -200,75 +224,112 @@ static void ics_set_irq_msi(void *opaque, int srcno, int val) } } -static void ics_reject_msi(struct ics_state *ics, int nr) +static void set_irq_lsi(struct ics_state *ics, int srcno, int val) { - struct ics_irq_state *irq = ics->irqs + nr - ics->offset; + struct ics_irq_state *irq = ics->irqs + srcno; - irq->rejected = 1; + irq->asserted = val; + resend_lsi(ics, srcno); } -static void ics_resend_msi(struct ics_state *ics) +static void ics_set_irq(void *opaque, int srcno, int val) { - int i; + struct ics_state *ics = (struct ics_state *)opaque; + struct ics_irq_state *irq = ics->irqs + srcno; - for (i = 0; i < ics->nr_irqs; i++) { - struct ics_irq_state *irq = ics->irqs + i; + if (irq->type == XICS_LSI) { + set_irq_lsi(ics, srcno, val); + } else { + set_irq_msi(ics, srcno, val); + } +} - /* FIXME: filter by server#? */ - if (irq->rejected) { - irq->rejected = 0; - if (irq->priority != 0xff) { - icp_irq(ics->icp, irq->server, i + ics->offset, irq->priority); - } - } +static void write_xive_msi(struct ics_state *ics, int srcno) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + if (!irq->masked_pending || (irq->priority == 0xff)) { + return; } + + irq->masked_pending = 0; + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); } -static void ics_write_xive_msi(struct ics_state *ics, int nr, int server, - uint8_t priority) +static void write_xive_lsi(struct ics_state *ics, int srcno) { - struct ics_irq_state *irq = ics->irqs + nr - ics->offset; + resend_lsi(ics, srcno); +} + +static void ics_write_xive(struct ics_state *ics, int nr, int server, + uint8_t priority) +{ + int srcno = nr - ics->offset; + struct ics_irq_state *irq = ics->irqs + srcno; irq->server = server; irq->priority = priority; - if (!irq->masked_pending || (priority == 0xff)) { - return; + if (irq->type == XICS_LSI) { + write_xive_lsi(ics, srcno); + } else { + write_xive_msi(ics, srcno); } - - irq->masked_pending = 0; - icp_irq(ics->icp, server, nr, priority); } static void ics_reject(struct ics_state *ics, int nr) { - ics_reject_msi(ics, nr); + struct ics_irq_state *irq = ics->irqs + nr - ics->offset; + + irq->rejected = 1; /* Irrelevant but harmless for LSI */ + irq->sent = 0; /* Irrelevant but harmless for MSI */ } static void ics_resend(struct ics_state *ics) { - ics_resend_msi(ics); + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + struct ics_irq_state *irq = ics->irqs + i; + + /* FIXME: filter by server#? */ + if (irq->type == XICS_LSI) { + resend_lsi(ics, i); + } else { + resend_msi(ics, i); + } + } } static void ics_eoi(struct ics_state *ics, int nr) { + int srcno = nr - ics->offset; + struct ics_irq_state *irq = ics->irqs + srcno; + + if (irq->type == XICS_LSI) { + irq->sent = 0; + } } /* * Exported functions */ -qemu_irq xics_find_qirq(struct icp_state *icp, int irq) +qemu_irq xics_assign_irq(struct icp_state *icp, int irq, + enum xics_irq_type type) { if ((irq < icp->ics->offset) || (irq >= (icp->ics->offset + icp->ics->nr_irqs))) { return NULL; } + 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(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_cppr(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong cppr = args[0]; @@ -277,7 +338,7 @@ static target_ulong h_cppr(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_ipi(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_ipi(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong server = args[0]; @@ -292,7 +353,7 @@ static target_ulong h_ipi(CPUState *env, sPAPREnvironment *spapr, } -static target_ulong h_xirr(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_xirr(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { uint32_t xirr = icp_accept(spapr->icp->ss + env->cpu_index); @@ -301,7 +362,7 @@ static target_ulong h_xirr(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_eoi(CPUState *env, sPAPREnvironment *spapr, +static target_ulong h_eoi(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong xirr = args[0]; @@ -332,7 +393,7 @@ static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token, return; } - ics_write_xive_msi(ics, nr, server, priority); + ics_write_xive(ics, nr, server, priority); rtas_st(rets, 0, 0); /* Success */ } @@ -424,7 +485,7 @@ static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token, struct icp_state *xics_system_init(int nr_irqs) { - CPUState *env; + CPUPPCState *env; int max_server_num; int i; struct icp_state *icp; @@ -477,7 +538,7 @@ struct icp_state *xics_system_init(int nr_irqs) ics->irqs[i].saved_priority = 0xff; } - ics->qirqs = qemu_allocate_irqs(ics_set_irq_msi, ics, nr_irqs); + ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, nr_irqs); spapr_register_hypercall(H_CPPR, h_cppr); spapr_register_hypercall(H_IPI, h_ipi); diff --git a/hw/xics.h b/hw/xics.h index 83c1182598..208015939c 100644 --- a/hw/xics.h +++ b/hw/xics.h @@ -31,7 +31,13 @@ struct icp_state; -qemu_irq xics_find_qirq(struct icp_state *icp, int irq); +enum xics_irq_type { + XICS_MSI, /* Message-signalled (edge) interrupt */ + XICS_LSI, /* Level-signalled interrupt */ +}; + +qemu_irq xics_assign_irq(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_zynq.c b/hw/xilinx_zynq.c new file mode 100644 index 0000000000..7290c64a4c --- /dev/null +++ b/hw/xilinx_zynq.c @@ -0,0 +1,157 @@ +/* + * Xilinx Zynq Baseboard System emulation. + * + * Copyright (c) 2010 Xilinx. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.croshtwaite@petalogix.com) + * Copyright (c) 2012 Petalogix Pty Ltd. + * Written by Haibing Ma + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "arm-misc.h" +#include "net.h" +#include "exec-memory.h" +#include "sysemu.h" +#include "boards.h" +#include "flash.h" +#include "blockdev.h" +#include "loader.h" + +#define FLASH_SIZE (64 * 1024 * 1024) +#define FLASH_SECTOR_SIZE (128 * 1024) + +#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ + +static struct arm_boot_info zynq_binfo = {}; + +static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq) +{ + DeviceState *dev; + SysBusDevice *s; + + qemu_check_nic_model(nd, "cadence_gem"); + dev = qdev_create(NULL, "cadence_gem"); + qdev_set_nic_properties(dev, nd); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + sysbus_mmio_map(s, 0, base); + sysbus_connect_irq(s, 0, irq); +} + +static void zynq_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) +{ + CPUARMState *env = NULL; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ext_ram = g_new(MemoryRegion, 1); + MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); + DeviceState *dev; + SysBusDevice *busdev; + qemu_irq *irqp; + qemu_irq pic[64]; + NICInfo *nd; + int n; + qemu_irq cpu_irq; + + if (!cpu_model) { + cpu_model = "cortex-a9"; + } + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + irqp = arm_pic_init_cpu(env); + cpu_irq = irqp[ARM_PIC_CPU_IRQ]; + + /* max 2GB ram */ + if (ram_size > 0x80000000) { + ram_size = 0x80000000; + } + + /* DDR remapped to address zero. */ + memory_region_init_ram(ext_ram, "zynq.ext_ram", ram_size); + vmstate_register_ram_global(ext_ram); + memory_region_add_subregion(address_space_mem, 0, ext_ram); + + /* 256K of on-chip memory */ + memory_region_init_ram(ocm_ram, "zynq.ocm_ram", 256 << 10); + vmstate_register_ram_global(ocm_ram); + memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram); + + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); + + /* AMD */ + pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE, + dinfo ? dinfo->bdrv : NULL, FLASH_SECTOR_SIZE, + FLASH_SIZE/FLASH_SECTOR_SIZE, 1, + 1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa, + 0); + + dev = qdev_create(NULL, "xilinx,zynq_slcr"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xF8000000); + + dev = qdev_create(NULL, "a9mpcore_priv"); + qdev_prop_set_uint32(dev, "num-cpu", 1); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + sysbus_mmio_map(busdev, 0, 0xF8F00000); + sysbus_connect_irq(busdev, 0, cpu_irq); + + for (n = 0; n < 64; n++) { + pic[n] = qdev_get_gpio_in(dev, n); + } + + sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]); + sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]); + + sysbus_create_varargs("cadence_ttc", 0xF8001000, + pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); + sysbus_create_varargs("cadence_ttc", 0xF8002000, + pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL); + + for (n = 0; n < nb_nics; n++) { + nd = &nd_table[n]; + if (n == 0) { + gem_init(nd, 0xE000B000, pic[54-IRQ_OFFSET]); + } else if (n == 1) { + gem_init(nd, 0xE000C000, pic[77-IRQ_OFFSET]); + } + } + + zynq_binfo.ram_size = ram_size; + zynq_binfo.kernel_filename = kernel_filename; + zynq_binfo.kernel_cmdline = kernel_cmdline; + zynq_binfo.initrd_filename = initrd_filename; + zynq_binfo.nb_cpus = 1; + zynq_binfo.board_id = 0xd32; + zynq_binfo.loader_start = 0; + arm_load_kernel(first_cpu, &zynq_binfo); +} + +static QEMUMachine zynq_machine = { + .name = "xilinx-zynq-a9", + .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9", + .init = zynq_init, + .use_scsi = 1, + .max_cpus = 1, + .no_sdcard = 1 +}; + +static void zynq_machine_init(void) +{ + qemu_register_machine(&zynq_machine); +} + +machine_init(zynq_machine_init); diff --git a/hw/xtensa_lx60.c b/hw/xtensa_lx60.c index 26112c3eb0..afdef494b2 100644 --- a/hw/xtensa_lx60.c +++ b/hw/xtensa_lx60.c @@ -146,9 +146,11 @@ static uint64_t translate_phys_addr(void *env, uint64_t addr) return cpu_get_phys_page_debug(env, addr); } -static void lx60_reset(void *env) +static void lx60_reset(void *opaque) { - cpu_reset(env); + CPUXtensaState *env = opaque; + + cpu_state_reset(env); } static void lx_init(const LxBoardDesc *board, @@ -162,7 +164,7 @@ static void lx_init(const LxBoardDesc *board, int be = 0; #endif MemoryRegion *system_memory = get_system_memory(); - CPUState *env = NULL; + CPUXtensaState *env = NULL; MemoryRegion *ram, *rom, *system_io; DriveInfo *dinfo; pflash_t *flash = NULL; @@ -183,7 +185,7 @@ static void lx_init(const LxBoardDesc *board, /* Need MMU initialized prior to ELF loading, * so that ELF gets loaded into virtual addresses */ - cpu_reset(env); + cpu_state_reset(env); } ram = g_malloc(sizeof(*ram)); diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c index 71d5fc89d4..653ded6820 100644 --- a/hw/xtensa_pic.c +++ b/hw/xtensa_pic.c @@ -29,7 +29,7 @@ #include "qemu-log.h" #include "qemu-timer.h" -void xtensa_advance_ccount(CPUState *env, uint32_t d) +void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d) { uint32_t old_ccount = env->sregs[CCOUNT]; @@ -45,7 +45,7 @@ void xtensa_advance_ccount(CPUState *env, uint32_t d) } } -void check_interrupts(CPUState *env) +void check_interrupts(CPUXtensaState *env) { int minlevel = xtensa_get_cintlevel(env); uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; @@ -84,7 +84,7 @@ void check_interrupts(CPUState *env) static void xtensa_set_irq(void *opaque, int irq, int active) { - CPUState *env = opaque; + CPUXtensaState *env = opaque; if (irq >= env->config->ninterrupt) { qemu_log("%s: bad IRQ %d\n", __func__, irq); @@ -101,12 +101,12 @@ static void xtensa_set_irq(void *opaque, int irq, int active) } } -void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active) +void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active) { qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); } -void xtensa_rearm_ccompare_timer(CPUState *env) +void xtensa_rearm_ccompare_timer(CPUXtensaState *env) { int i; uint32_t wake_ccount = env->sregs[CCOUNT] - 1; @@ -125,7 +125,7 @@ void xtensa_rearm_ccompare_timer(CPUState *env) static void xtensa_ccompare_cb(void *opaque) { - CPUState *env = opaque; + CPUXtensaState *env = opaque; if (env->halted) { env->halt_clock = qemu_get_clock_ns(vm_clock); @@ -137,7 +137,7 @@ static void xtensa_ccompare_cb(void *opaque) } } -void xtensa_irq_init(CPUState *env) +void xtensa_irq_init(CPUXtensaState *env) { env->irq_inputs = (void **)qemu_allocate_irqs( xtensa_set_irq, env, env->config->ninterrupt); @@ -148,7 +148,7 @@ void xtensa_irq_init(CPUState *env) } } -void *xtensa_get_extint(CPUState *env, unsigned extint) +void *xtensa_get_extint(CPUXtensaState *env, unsigned extint) { if (extint < env->config->nextint) { unsigned irq = env->config->extint[extint]; diff --git a/hw/xtensa_sim.c b/hw/xtensa_sim.c index 104e5dc619..c7e05dcf4e 100644 --- a/hw/xtensa_sim.c +++ b/hw/xtensa_sim.c @@ -39,7 +39,7 @@ static uint64_t translate_phys_addr(void *env, uint64_t addr) static void sim_reset(void *env) { - cpu_reset(env); + cpu_state_reset(env); } static void sim_init(ram_addr_t ram_size, @@ -47,7 +47,7 @@ static void sim_init(ram_addr_t ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env = NULL; + CPUXtensaState *env = NULL; MemoryRegion *ram, *rom; int n; diff --git a/hw/zynq_slcr.c b/hw/zynq_slcr.c new file mode 100644 index 0000000000..4f97575770 --- /dev/null +++ b/hw/zynq_slcr.c @@ -0,0 +1,535 @@ +/* + * Status and system control registers for Xilinx Zynq Platform + * + * Copyright (c) 2011 Michal Simek <monstr@monstr.eu> + * Copyright (c) 2012 PetaLogix Pty Ltd. + * Based on hw/arm_sysctl.c, written by Paul Brook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw.h" +#include "qemu-timer.h" +#include "sysbus.h" +#include "sysemu.h" + +#ifdef ZYNQ_ARM_SLCR_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +#define XILINX_LOCK_KEY 0x767b +#define XILINX_UNLOCK_KEY 0xdf0d + +typedef enum { + ARM_PLL_CTRL, + DDR_PLL_CTRL, + IO_PLL_CTRL, + PLL_STATUS, + ARM_PPL_CFG, + DDR_PLL_CFG, + IO_PLL_CFG, + PLL_BG_CTRL, + PLL_MAX +} PLLValues; + +typedef enum { + ARM_CLK_CTRL, + DDR_CLK_CTRL, + DCI_CLK_CTRL, + APER_CLK_CTRL, + USB0_CLK_CTRL, + USB1_CLK_CTRL, + GEM0_RCLK_CTRL, + GEM1_RCLK_CTRL, + GEM0_CLK_CTRL, + GEM1_CLK_CTRL, + SMC_CLK_CTRL, + LQSPI_CLK_CTRL, + SDIO_CLK_CTRL, + UART_CLK_CTRL, + SPI_CLK_CTRL, + CAN_CLK_CTRL, + CAN_MIOCLK_CTRL, + DBG_CLK_CTRL, + PCAP_CLK_CTRL, + TOPSW_CLK_CTRL, + CLK_MAX +} ClkValues; + +typedef enum { + CLK_CTRL, + THR_CTRL, + THR_CNT, + THR_STA, + FPGA_MAX +} FPGAValues; + +typedef enum { + SYNC_CTRL, + SYNC_STATUS, + BANDGAP_TRIP, + CC_TEST, + PLL_PREDIVISOR, + CLK_621_TRUE, + PICTURE_DBG, + PICTURE_DBG_UCNT, + PICTURE_DBG_LCNT, + MISC_MAX +} MiscValues; + +typedef enum { + PSS, + DDDR, + DMAC, + USB, + GEM, + SDIO, + SPI, + CAN, + I2C, + UART, + GPIO, + LQSPI, + SMC, + OCM, + DEVCI, + FPGA, + A9_CPU, + RS_AWDT, + RST_REASON, + RST_REASON_CLR, + REBOOT_STATUS, + BOOT_MODE, + RESET_MAX +} ResetValues; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + + union { + struct { + uint16_t scl; + uint16_t lockval; + uint32_t pll[PLL_MAX]; /* 0x100 - 0x11C */ + uint32_t clk[CLK_MAX]; /* 0x120 - 0x16C */ + uint32_t fpga[4][FPGA_MAX]; /* 0x170 - 0x1AC */ + uint32_t misc[MISC_MAX]; /* 0x1B0 - 0x1D8 */ + uint32_t reset[RESET_MAX]; /* 0x200 - 0x25C */ + uint32_t apu_ctrl; /* 0x300 */ + uint32_t wdt_clk_sel; /* 0x304 */ + uint32_t tz_ocm[3]; /* 0x400 - 0x408 */ + uint32_t tz_ddr; /* 0x430 */ + uint32_t tz_dma[3]; /* 0x440 - 0x448 */ + uint32_t tz_misc[3]; /* 0x450 - 0x458 */ + uint32_t tz_fpga[2]; /* 0x484 - 0x488 */ + uint32_t dbg_ctrl; /* 0x500 */ + uint32_t pss_idcode; /* 0x530 */ + uint32_t ddr[8]; /* 0x600 - 0x620 - 0x604-missing */ + uint32_t mio[54]; /* 0x700 - 0x7D4 */ + uint32_t mio_func[4]; /* 0x800 - 0x810 */ + uint32_t sd[2]; /* 0x830 - 0x834 */ + uint32_t lvl_shftr_en; /* 0x900 */ + uint32_t ocm_cfg; /* 0x910 */ + uint32_t cpu_ram[8]; /* 0xA00 - 0xA1C */ + uint32_t iou[7]; /* 0xA30 - 0xA48 */ + uint32_t dmac_ram; /* 0xA50 */ + uint32_t afi[4][3]; /* 0xA60 - 0xA8C */ + uint32_t ocm[3]; /* 0xA90 - 0xA98 */ + uint32_t devci_ram; /* 0xAA0 */ + uint32_t csg_ram; /* 0xAB0 */ + uint32_t gpiob[12]; /* 0xB00 - 0xB2C */ + uint32_t ddriob[14]; /* 0xB40 - 0xB74 */ + }; + uint8_t data[0x1000]; + }; +} ZynqSLCRState; + +static void zynq_slcr_reset(DeviceState *d) +{ + int i; + ZynqSLCRState *s = + FROM_SYSBUS(ZynqSLCRState, sysbus_from_qdev(d)); + + DB_PRINT("RESET\n"); + + s->lockval = 1; + /* 0x100 - 0x11C */ + s->pll[ARM_PLL_CTRL] = 0x0001A008; + s->pll[DDR_PLL_CTRL] = 0x0001A008; + s->pll[IO_PLL_CTRL] = 0x0001A008; + s->pll[PLL_STATUS] = 0x0000003F; + s->pll[ARM_PPL_CFG] = 0x00014000; + s->pll[DDR_PLL_CFG] = 0x00014000; + s->pll[IO_PLL_CFG] = 0x00014000; + + /* 0x120 - 0x16C */ + s->clk[ARM_CLK_CTRL] = 0x1F000400; + s->clk[DDR_CLK_CTRL] = 0x18400003; + s->clk[DCI_CLK_CTRL] = 0x01E03201; + s->clk[APER_CLK_CTRL] = 0x01FFCCCD; + s->clk[USB0_CLK_CTRL] = s->clk[USB1_CLK_CTRL] = 0x00101941; + s->clk[GEM0_RCLK_CTRL] = s->clk[GEM1_RCLK_CTRL] = 0x00000001; + s->clk[GEM0_CLK_CTRL] = s->clk[GEM1_CLK_CTRL] = 0x00003C01; + s->clk[SMC_CLK_CTRL] = 0x00003C01; + s->clk[LQSPI_CLK_CTRL] = 0x00002821; + s->clk[SDIO_CLK_CTRL] = 0x00001E03; + s->clk[UART_CLK_CTRL] = 0x00003F03; + s->clk[SPI_CLK_CTRL] = 0x00003F03; + s->clk[CAN_CLK_CTRL] = 0x00501903; + s->clk[DBG_CLK_CTRL] = 0x00000F03; + s->clk[PCAP_CLK_CTRL] = 0x00000F01; + + /* 0x170 - 0x1AC */ + s->fpga[0][CLK_CTRL] = s->fpga[1][CLK_CTRL] = s->fpga[2][CLK_CTRL] = + s->fpga[3][CLK_CTRL] = 0x00101800; + s->fpga[0][THR_STA] = s->fpga[1][THR_STA] = s->fpga[2][THR_STA] = + s->fpga[3][THR_STA] = 0x00010000; + + /* 0x1B0 - 0x1D8 */ + s->misc[BANDGAP_TRIP] = 0x0000001F; + s->misc[PLL_PREDIVISOR] = 0x00000001; + s->misc[CLK_621_TRUE] = 0x00000001; + + /* 0x200 - 0x25C */ + s->reset[FPGA] = 0x01F33F0F; + s->reset[RST_REASON] = 0x00000040; + + /* 0x700 - 0x7D4 */ + for (i = 0; i < 54; i++) { + s->mio[i] = 0x00001601; + } + for (i = 2; i <= 8; i++) { + s->mio[i] = 0x00000601; + } + + /* MIO_MST_TRI0, MIO_MST_TRI1 */ + s->mio_func[2] = s->mio_func[3] = 0xFFFFFFFF; + + s->cpu_ram[0] = s->cpu_ram[1] = s->cpu_ram[3] = + s->cpu_ram[4] = s->cpu_ram[7] = 0x00010101; + s->cpu_ram[2] = s->cpu_ram[5] = 0x01010101; + s->cpu_ram[6] = 0x00000001; + + s->iou[0] = s->iou[1] = s->iou[2] = s->iou[3] = 0x09090909; + s->iou[4] = s->iou[5] = 0x00090909; + s->iou[6] = 0x00000909; + + s->dmac_ram = 0x00000009; + + s->afi[0][0] = s->afi[0][1] = 0x09090909; + s->afi[1][0] = s->afi[1][1] = 0x09090909; + s->afi[2][0] = s->afi[2][1] = 0x09090909; + s->afi[3][0] = s->afi[3][1] = 0x09090909; + s->afi[0][2] = s->afi[1][2] = s->afi[2][2] = s->afi[3][2] = 0x00000909; + + s->ocm[0] = 0x01010101; + s->ocm[1] = s->ocm[2] = 0x09090909; + + s->devci_ram = 0x00000909; + s->csg_ram = 0x00000001; + + s->ddriob[0] = s->ddriob[1] = s->ddriob[2] = s->ddriob[3] = 0x00000e00; + s->ddriob[4] = s->ddriob[5] = s->ddriob[6] = 0x00000e00; + s->ddriob[12] = 0x00000021; +} + +static inline uint32_t zynq_slcr_read_imp(void *opaque, + target_phys_addr_t offset) +{ + ZynqSLCRState *s = (ZynqSLCRState *)opaque; + + switch (offset) { + case 0x0: /* SCL */ + return s->scl; + case 0x4: /* LOCK */ + case 0x8: /* UNLOCK */ + DB_PRINT("Reading SCLR_LOCK/UNLOCK is not enabled\n"); + return 0; + case 0x0C: /* LOCKSTA */ + return s->lockval; + case 0x100 ... 0x11C: + return s->pll[(offset - 0x100) / 4]; + case 0x120 ... 0x16C: + return s->clk[(offset - 0x120) / 4]; + case 0x170 ... 0x1AC: + return s->fpga[0][(offset - 0x170) / 4]; + case 0x1B0 ... 0x1D8: + return s->misc[(offset - 0x1B0) / 4]; + case 0x200 ... 0x258: + return s->reset[(offset - 0x200) / 4]; + case 0x25c: + return 1; + case 0x300: + return s->apu_ctrl; + case 0x304: + return s->wdt_clk_sel; + case 0x400 ... 0x408: + return s->tz_ocm[(offset - 0x400) / 4]; + case 0x430: + return s->tz_ddr; + case 0x440 ... 0x448: + return s->tz_dma[(offset - 0x440) / 4]; + case 0x450 ... 0x458: + return s->tz_misc[(offset - 0x450) / 4]; + case 0x484 ... 0x488: + return s->tz_fpga[(offset - 0x484) / 4]; + case 0x500: + return s->dbg_ctrl; + case 0x530: + return s->pss_idcode; + case 0x600 ... 0x620: + if (offset == 0x604) { + goto bad_reg; + } + return s->ddr[(offset - 0x600) / 4]; + case 0x700 ... 0x7D4: + return s->mio[(offset - 0x700) / 4]; + case 0x800 ... 0x810: + return s->mio_func[(offset - 0x800) / 4]; + case 0x830 ... 0x834: + return s->sd[(offset - 0x830) / 4]; + case 0x900: + return s->lvl_shftr_en; + case 0x910: + return s->ocm_cfg; + case 0xA00 ... 0xA1C: + return s->cpu_ram[(offset - 0xA00) / 4]; + case 0xA30 ... 0xA48: + return s->iou[(offset - 0xA30) / 4]; + case 0xA50: + return s->dmac_ram; + case 0xA60 ... 0xA8C: + return s->afi[0][(offset - 0xA60) / 4]; + case 0xA90 ... 0xA98: + return s->ocm[(offset - 0xA90) / 4]; + case 0xAA0: + return s->devci_ram; + case 0xAB0: + return s->csg_ram; + case 0xB00 ... 0xB2C: + return s->gpiob[(offset - 0xB00) / 4]; + case 0xB40 ... 0xB74: + return s->ddriob[(offset - 0xB40) / 4]; + default: + bad_reg: + DB_PRINT("Bad register offset 0x%x\n", (int)offset); + return 0; + } +} + +static uint64_t zynq_slcr_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + uint32_t ret = zynq_slcr_read_imp(opaque, offset); + + DB_PRINT("addr: %08x data: %08x\n", offset, ret); + return ret; +} + +static void zynq_slcr_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + ZynqSLCRState *s = (ZynqSLCRState *)opaque; + + DB_PRINT("offset: %08x data: %08x\n", offset, (unsigned)val); + + switch (offset) { + case 0x00: /* SCL */ + s->scl = val & 0x1; + return; + case 0x4: /* SLCR_LOCK */ + if ((val & 0xFFFF) == XILINX_LOCK_KEY) { + DB_PRINT("XILINX LOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, + (unsigned)val & 0xFFFF); + s->lockval = 1; + } else { + DB_PRINT("WRONG XILINX LOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", + (int)offset, (unsigned)val & 0xFFFF); + } + return; + case 0x8: /* SLCR_UNLOCK */ + if ((val & 0xFFFF) == XILINX_UNLOCK_KEY) { + DB_PRINT("XILINX UNLOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, + (unsigned)val & 0xFFFF); + s->lockval = 0; + } else { + DB_PRINT("WRONG XILINX UNLOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", + (int)offset, (unsigned)val & 0xFFFF); + } + return; + case 0xc: /* LOCKSTA */ + DB_PRINT("Writing SCLR_LOCKSTA is not enabled\n"); + return; + } + + if (!s->lockval) { + switch (offset) { + case 0x100 ... 0x11C: + if (offset == 0x10C) { + goto bad_reg; + } + s->pll[(offset - 0x100) / 4] = val; + break; + case 0x120 ... 0x16C: + s->clk[(offset - 0x120) / 4] = val; + break; + case 0x170 ... 0x1AC: + s->fpga[0][(offset - 0x170) / 4] = val; + break; + case 0x1B0 ... 0x1D8: + s->misc[(offset - 0x1B0) / 4] = val; + break; + case 0x200 ... 0x25C: + if (offset == 0x250) { + goto bad_reg; + } + s->reset[(offset - 0x200) / 4] = val; + break; + case 0x300: + s->apu_ctrl = val; + break; + case 0x304: + s->wdt_clk_sel = val; + break; + case 0x400 ... 0x408: + s->tz_ocm[(offset - 0x400) / 4] = val; + break; + case 0x430: + s->tz_ddr = val; + break; + case 0x440 ... 0x448: + s->tz_dma[(offset - 0x440) / 4] = val; + break; + case 0x450 ... 0x458: + s->tz_misc[(offset - 0x450) / 4] = val; + break; + case 0x484 ... 0x488: + s->tz_fpga[(offset - 0x484) / 4] = val; + break; + case 0x500: + s->dbg_ctrl = val; + break; + case 0x530: + s->pss_idcode = val; + break; + case 0x600 ... 0x620: + if (offset == 0x604) { + goto bad_reg; + } + s->ddr[(offset - 0x600) / 4] = val; + break; + case 0x700 ... 0x7D4: + s->mio[(offset - 0x700) / 4] = val; + break; + case 0x800 ... 0x810: + s->mio_func[(offset - 0x800) / 4] = val; + break; + case 0x830 ... 0x834: + s->sd[(offset - 0x830) / 4] = val; + break; + case 0x900: + s->lvl_shftr_en = val; + break; + case 0x910: + break; + case 0xA00 ... 0xA1C: + s->cpu_ram[(offset - 0xA00) / 4] = val; + break; + case 0xA30 ... 0xA48: + s->iou[(offset - 0xA30) / 4] = val; + break; + case 0xA50: + s->dmac_ram = val; + break; + case 0xA60 ... 0xA8C: + s->afi[0][(offset - 0xA60) / 4] = val; + break; + case 0xA90: + s->ocm[0] = val; + break; + case 0xAA0: + s->devci_ram = val; + break; + case 0xAB0: + s->csg_ram = val; + break; + case 0xB00 ... 0xB2C: + if (offset == 0xB20 || offset == 0xB2C) { + goto bad_reg; + } + s->gpiob[(offset - 0xB00) / 4] = val; + break; + case 0xB40 ... 0xB74: + s->ddriob[(offset - 0xB40) / 4] = val; + break; + default: + bad_reg: + DB_PRINT("Bad register write %x <= %08x\n", (int)offset, val); + } + } else { + DB_PRINT("SCLR registers are locked. Unlock them first\n"); + } +} + +static const MemoryRegionOps slcr_ops = { + .read = zynq_slcr_read, + .write = zynq_slcr_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int zynq_slcr_init(SysBusDevice *dev) +{ + ZynqSLCRState *s = FROM_SYSBUS(ZynqSLCRState, dev); + + memory_region_init_io(&s->iomem, &slcr_ops, s, "slcr", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static const VMStateDescription vmstate_zynq_slcr = { + .name = "zynq_slcr", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(data, ZynqSLCRState, 0x1000), + VMSTATE_END_OF_LIST() + } +}; + +static void zynq_slcr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = zynq_slcr_init; + dc->vmsd = &vmstate_zynq_slcr; + dc->reset = zynq_slcr_reset; +} + +static TypeInfo zynq_slcr_info = { + .class_init = zynq_slcr_class_init, + .name = "xilinx,zynq_slcr", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ZynqSLCRState), +}; + +static void zynq_slcr_register_types(void) +{ + type_register_static(&zynq_slcr_info); +} + +type_init(zynq_slcr_register_types) |