diff options
Diffstat (limited to 'hw')
88 files changed, 3568 insertions, 1628 deletions
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 45e9a1f9b0..f3ebca4f7a 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1098,8 +1098,13 @@ static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, { if (dir_path) { v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); - } else { + } else if (strcmp(name, "/")) { v9fs_path_sprintf(target, "%s", name); + } else { + /* We want the path of the export root to be relative, otherwise + * "*at()" syscalls would treat it as "/" in the host. + */ + v9fs_path_sprintf(target, "%s", "."); } return 0; } diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c index eec160b3c2..d05c1a1c1d 100644 --- a/hw/9pfs/9p-xattr.c +++ b/hw/9pfs/9p-xattr.c @@ -108,6 +108,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, g_free(name); close_preserve_errno(dirfd); if (xattr_len < 0) { + g_free(orig_value); return -1; } diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index b8c0b99358..c80ba67389 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -539,14 +539,15 @@ static void coroutine_fn virtfs_reset(V9fsPDU *pdu) /* Free all fids */ while (s->fid_list) { + /* Get fid */ fidp = s->fid_list; + fidp->ref++; + + /* Clunk fid */ s->fid_list = fidp->next; + fidp->clunked = 1; - if (fidp->ref) { - fidp->clunked = 1; - } else { - free_fid(pdu, fidp); - } + put_fid(pdu, fidp); } } @@ -1550,6 +1551,10 @@ static void coroutine_fn v9fs_lcreate(void *opaque) err = -ENOENT; goto out_nofid; } + if (fidp->fid_type != P9_FID_NONE) { + err = -EINVAL; + goto out; + } flags = get_dotl_openflags(pdu->s, flags); err = v9fs_co_open2(pdu, fidp, &name, gid, @@ -2153,6 +2158,10 @@ static void coroutine_fn v9fs_create(void *opaque) err = -EINVAL; goto out_nofid; } + if (fidp->fid_type != P9_FID_NONE) { + err = -EINVAL; + goto out; + } if (perm & P9_STAT_MODE_DIR) { err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777, fidp->uid, -1, &stbuf); @@ -2379,8 +2388,10 @@ static void coroutine_fn v9fs_flush(void *opaque) * Wait for pdu to complete. */ qemu_co_queue_wait(&cancel_pdu->complete, NULL); - cancel_pdu->cancelled = 0; - pdu_free(cancel_pdu); + if (!qemu_co_queue_next(&cancel_pdu->complete)) { + cancel_pdu->cancelled = 0; + pdu_free(cancel_pdu); + } } pdu_complete(pdu, 7); } diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c index b4adac88cd..05b9d7ba36 100644 --- a/hw/acpi/tco.c +++ b/hw/acpi/tco.c @@ -75,8 +75,6 @@ static void tco_timer_expired(void *opaque) if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) { ich9_generate_smi(); - } else { - ich9_generate_nmi(); } tr->tco.rld = tr->tco.tmr; tco_timer_reload(tr); diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 7a3ad17d66..a32b847fe0 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -205,9 +205,30 @@ static void vmgenid_handle_reset(void *opaque) memset(vms->vmgenid_addr_le, 0, ARRAY_SIZE(vms->vmgenid_addr_le)); } +static Property vmgenid_properties[] = { + DEFINE_PROP_BOOL("x-write-pointer-available", VmGenIdState, + write_pointer_available, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void vmgenid_realize(DeviceState *dev, Error **errp) { VmGenIdState *vms = VMGENID(dev); + + if (!vms->write_pointer_available) { + error_setg(errp, "%s requires DMA write support in fw_cfg, " + "which this machine type does not provide", VMGENID_DEVICE); + return; + } + + /* Given that this function is executing, there is at least one VMGENID + * device. Check if there are several. + */ + if (!find_vmgenid_dev()) { + error_setg(errp, "at most one %s device is permitted", VMGENID_DEVICE); + return; + } + qemu_register_reset(vmgenid_handle_reset, vms); } @@ -218,6 +239,7 @@ static void vmgenid_device_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_vmgenid; dc->realize = vmgenid_realize; dc->hotpluggable = false; + dc->props = vmgenid_properties; object_class_property_add_str(klass, VMGENID_GUID, NULL, vmgenid_set_guid, NULL); diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index ca15d1c8cc..f62a9a3541 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -118,12 +118,6 @@ static void aw_a10_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = aw_a10_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo aw_a10_type_info = { diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 571e4f097b..4937e2bc83 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -19,6 +19,7 @@ #include "hw/char/serial.h" #include "qemu/log.h" #include "hw/i2c/aspeed_i2c.h" +#include "net/net.h" #define ASPEED_SOC_UART_5_BASE 0x00184000 #define ASPEED_SOC_IOMEM_SIZE 0x00200000 @@ -33,6 +34,8 @@ #define ASPEED_SOC_TIMER_BASE 0x1E782000 #define ASPEED_SOC_WDT_BASE 0x1E785000 #define ASPEED_SOC_I2C_BASE 0x1E78A000 +#define ASPEED_SOC_ETH1_BASE 0x1E660000 +#define ASPEED_SOC_ETH2_BASE 0x1E680000 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, }; @@ -175,6 +178,10 @@ static void aspeed_soc_init(Object *obj) object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ASPEED_WDT); object_property_add_child(obj, "wdt", OBJECT(&s->wdt), NULL); qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); + + object_initialize(&s->ftgmac100, sizeof(s->ftgmac100), TYPE_FTGMAC100); + object_property_add_child(obj, "ftgmac100", OBJECT(&s->ftgmac100), NULL); + qdev_set_parent_bus(DEVICE(&s->ftgmac100), sysbus_get_default()); } static void aspeed_soc_realize(DeviceState *dev, Error **errp) @@ -299,6 +306,20 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE); + + /* Net */ + qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]); + object_property_set_bool(OBJECT(&s->ftgmac100), true, "aspeed", &err); + object_property_set_bool(OBJECT(&s->ftgmac100), true, "realized", + &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100), 0, ASPEED_SOC_ETH1_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100), 0, + qdev_get_gpio_in(DEVICE(&s->vic), 2)); } static void aspeed_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 8451190a19..8c43291112 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -160,12 +160,6 @@ static void bcm2836_class_init(ObjectClass *oc, void *data) dc->props = bcm2836_props; dc->realize = bcm2836_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo bcm2836_type_info = { diff --git a/hw/arm/boot.c b/hw/arm/boot.c index ff621e4b6a..c2720c8046 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -31,6 +31,9 @@ #define KERNEL_LOAD_ADDR 0x00010000 #define KERNEL64_LOAD_ADDR 0x00080000 +#define ARM64_TEXT_OFFSET_OFFSET 8 +#define ARM64_MAGIC_OFFSET 56 + typedef enum { FIXUP_NONE = 0, /* do nothing */ FIXUP_TERMINATOR, /* end of insns */ @@ -768,6 +771,49 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, return ret; } +static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, + hwaddr *entry) +{ + hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR; + uint8_t *buffer; + int size; + + /* On aarch64, it's the bootloader's job to uncompress the kernel. */ + size = load_image_gzipped_buffer(filename, LOAD_IMAGE_MAX_GUNZIP_BYTES, + &buffer); + + if (size < 0) { + gsize len; + + /* Load as raw file otherwise */ + if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) { + return -1; + } + size = len; + } + + /* check the arm64 magic header value -- very old kernels may not have it */ + if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) { + uint64_t hdrvals[2]; + + /* The arm64 Image header has text_offset and image_size fields at 8 and + * 16 bytes into the Image header, respectively. The text_offset field + * is only valid if the image_size is non-zero. + */ + memcpy(&hdrvals, buffer + ARM64_TEXT_OFFSET_OFFSET, sizeof(hdrvals)); + if (hdrvals[1] != 0) { + kernel_load_offset = le64_to_cpu(hdrvals[0]); + } + } + + *entry = mem_base + kernel_load_offset; + rom_add_blob_fixed(filename, buffer, size, *entry); + + g_free(buffer); + + return size; +} + static void arm_load_kernel_notify(Notifier *notifier, void *data) { CPUState *cs; @@ -776,7 +822,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) int is_linux = 0; uint64_t elf_entry, elf_low_addr, elf_high_addr; int elf_machine; - hwaddr entry, kernel_load_offset; + hwaddr entry; static const ARMInsnFixup *primary_loader; ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, notifier, notifier); @@ -841,14 +887,12 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { primary_loader = bootloader_aarch64; - kernel_load_offset = KERNEL64_LOAD_ADDR; elf_machine = EM_AARCH64; } else { primary_loader = bootloader; if (!info->write_board_setup) { primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET; } - kernel_load_offset = KERNEL_LOAD_ADDR; elf_machine = EM_ARM; } @@ -900,17 +944,15 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) kernel_size = load_uimage(info->kernel_filename, &entry, NULL, &is_linux, NULL, NULL); } - /* On aarch64, it's the bootloader's job to uncompress the kernel. */ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) { - entry = info->loader_start + kernel_load_offset; - kernel_size = load_image_gzipped(info->kernel_filename, entry, - info->ram_size - kernel_load_offset); + kernel_size = load_aarch64_image(info->kernel_filename, + info->loader_start, &entry); is_linux = 1; - } - if (kernel_size < 0) { - entry = info->loader_start + kernel_load_offset; + } else if (kernel_size < 0) { + /* 32-bit ARM */ + entry = info->loader_start + KERNEL_LOAD_ADDR; kernel_size = load_image_targphys(info->kernel_filename, entry, - info->ram_size - kernel_load_offset); + info->ram_size - KERNEL_LOAD_ADDR); is_linux = 1; } if (kernel_size < 0) { diff --git a/hw/arm/digic.c b/hw/arm/digic.c index d60ea395f4..94f32637f0 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -101,12 +101,6 @@ static void digic_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = digic_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo digic_type_info = { diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 1d2b50cc4e..960f27e45a 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -32,6 +32,7 @@ #include "hw/arm/arm.h" #include "hw/loader.h" #include "hw/arm/exynos4210.h" +#include "hw/sd/sd.h" #include "hw/usb/hcd-ehci.h" #define EXYNOS4210_CHIPID_ADDR 0x10000000 @@ -72,6 +73,13 @@ #define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000 #define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000 +/* SD/MMC host controllers */ +#define EXYNOS4210_SDHCI_CAPABILITIES 0x05E80080 +#define EXYNOS4210_SDHCI_BASE_ADDR 0x12510000 +#define EXYNOS4210_SDHCI_ADDR(n) (EXYNOS4210_SDHCI_BASE_ADDR + \ + 0x00010000 * (n)) +#define EXYNOS4210_SDHCI_NUMBER 4 + /* PMU SFR base address */ #define EXYNOS4210_PMU_BASE_ADDR 0x10020000 @@ -382,6 +390,27 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, EXYNOS4210_UART3_FIFO_SIZE, 3, NULL, s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]); + /*** SD/MMC host controllers ***/ + for (n = 0; n < EXYNOS4210_SDHCI_NUMBER; n++) { + DeviceState *carddev; + BlockBackend *blk; + DriveInfo *di; + + dev = qdev_create(NULL, "generic-sdhci"); + qdev_prop_set_uint32(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES); + qdev_init_nofail(dev); + + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, EXYNOS4210_SDHCI_ADDR(n)); + sysbus_connect_irq(busdev, 0, s->irq_table[exynos4210_get_irq(29, n)]); + + di = drive_get(IF_SD, 0, n); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_abort); + qdev_init_nofail(carddev); + } + /*** Display controller (FIMD) ***/ sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR, s->irq_table[exynos4210_get_irq(11, 0)], diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 0efa194054..4853c31802 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu-common.h" #include "cpu.h" #include "sysemu/sysemu.h" @@ -101,9 +102,9 @@ static Exynos4210State *exynos4_boards_init_common(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) { - fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus" - " value.\n", - mc->name, EXYNOS4210_NCPUS); + error_report("%s board supports only %d CPU cores, ignoring smp_cpus" + " value", + mc->name, EXYNOS4210_NCPUS); } exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type]; diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 2126f73ca0..9056f27bf8 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -290,11 +290,6 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx25_realize; - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; dc->desc = "i.MX25 SOC"; } diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index dd1c713ae3..d7e2d832b2 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -262,11 +262,6 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx31_realize; - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; dc->desc = "i.MX31 SOC"; } diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 76dd8a48ca..6969e734ad 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -442,11 +442,6 @@ static void fsl_imx6_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx6_realize; - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; dc->desc = "i.MX6 SOC"; } diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index cfee3929d9..eea551dc16 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -755,19 +755,18 @@ static void pxa2xx_ssp_reset(DeviceState *d) s->rx_start = s->rx_level = 0; } -static int pxa2xx_ssp_init(SysBusDevice *sbd) +static void pxa2xx_ssp_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - PXA2xxSSPState *s = PXA2XX_SSP(dev); - + DeviceState *dev = DEVICE(obj); + PXA2xxSSPState *s = PXA2XX_SSP(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_ssp_ops, s, "pxa2xx-ssp", 0x1000); sysbus_init_mmio(sbd, &s->iomem); s->bus = ssi_create_bus(dev, "ssi"); - return 0; } /* Real-Time Clock */ @@ -2321,10 +2320,8 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = pxa2xx_ssp_init; dc->reset = pxa2xx_ssp_reset; dc->vmsd = &vmstate_pxa2xx_ssp; } @@ -2333,6 +2330,7 @@ static const TypeInfo pxa2xx_ssp_info = { .name = TYPE_PXA2XX_SSP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxSSPState), + .instance_init = pxa2xx_ssp_init, .class_init = pxa2xx_ssp_class_init, }; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 9edcd49740..ea7a8094e1 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -108,7 +108,10 @@ static void gptm_reload(gptm_state *s, int n, int reset) } else if (s->mode[n] == 0xa) { /* PWM mode. Not implemented. */ } else { - hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); + qemu_log_mask(LOG_UNIMP, + "GPTM: 16-bit timer mode unimplemented: 0x%x\n", + s->mode[n]); + return; } s->tick[n] = tick; timer_mod(s->timer[n], tick); @@ -149,7 +152,9 @@ static void gptm_tick(void *opaque) } else if (s->mode[n] == 0xa) { /* PWM mode. Not implemented. */ } else { - hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); + qemu_log_mask(LOG_UNIMP, + "GPTM: 16-bit timer mode unimplemented: 0x%x\n", + s->mode[n]); } gptm_update_irq(s); } @@ -286,7 +291,8 @@ static void gptm_write(void *opaque, hwaddr offset, s->match_prescale[0] = value; break; default: - hw_error("gptm_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "GPTM: read at bad offset 0x%x\n", (int)offset); } gptm_update_irq(s); } @@ -425,7 +431,10 @@ static int ssys_board_class(const ssys_state *s) } /* for unknown classes, fall through */ default: - hw_error("ssys_board_class: Unknown class 0x%08x\n", did0); + /* This can only happen if the hardwired constant did0 value + * in this board's stellaris_board_info struct is wrong. + */ + g_assert_not_reached(); } } @@ -479,8 +488,7 @@ static uint64_t ssys_read(void *opaque, hwaddr offset, case DID0_CLASS_SANDSTORM: return pllcfg_sandstorm[xtal]; default: - hw_error("ssys_read: Unhandled class for PLLCFG read.\n"); - return 0; + g_assert_not_reached(); } } case 0x070: /* RCC2 */ @@ -512,7 +520,8 @@ static uint64_t ssys_read(void *opaque, hwaddr offset, case 0x1e4: /* USER1 */ return s->user1; default: - hw_error("ssys_read: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "SSYS: read at bad offset 0x%x\n", (int)offset); return 0; } } @@ -614,7 +623,8 @@ static void ssys_write(void *opaque, hwaddr offset, s->ldoarst = value; break; default: - hw_error("ssys_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "SSYS: write at bad offset 0x%x\n", (int)offset); } ssys_update(s); } @@ -748,7 +758,8 @@ static uint64_t stellaris_i2c_read(void *opaque, hwaddr offset, case 0x20: /* MCR */ return s->mcr; default: - hw_error("strllaris_i2c_read: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_i2c: read at bad offset 0x%x\n", (int)offset); return 0; } } @@ -823,17 +834,18 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset, s->mris &= ~value; break; case 0x20: /* MCR */ - if (value & 1) - hw_error( - "stellaris_i2c_write: Loopback not implemented\n"); - if (value & 0x20) - hw_error( - "stellaris_i2c_write: Slave mode not implemented\n"); + if (value & 1) { + qemu_log_mask(LOG_UNIMP, "stellaris_i2c: Loopback not implemented"); + } + if (value & 0x20) { + qemu_log_mask(LOG_UNIMP, + "stellaris_i2c: Slave mode not implemented"); + } s->mcr = value & 0x31; break; default: - hw_error("stellaris_i2c_write: Bad offset 0x%x\n", - (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_i2c: write at bad offset 0x%x\n", (int)offset); } stellaris_i2c_update(s); } @@ -1057,8 +1069,8 @@ static uint64_t stellaris_adc_read(void *opaque, hwaddr offset, case 0x30: /* SAC */ return s->sac; default: - hw_error("strllaris_adc_read: Bad offset 0x%x\n", - (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_adc: read at bad offset 0x%x\n", (int)offset); return 0; } } @@ -1078,8 +1090,9 @@ static void stellaris_adc_write(void *opaque, hwaddr offset, return; case 0x04: /* SSCTL */ if (value != 6) { - hw_error("ADC: Unimplemented sequence %" PRIx64 "\n", - value); + qemu_log_mask(LOG_UNIMP, + "ADC: Unimplemented sequence %" PRIx64 "\n", + value); } s->ssctl[n] = value; return; @@ -1110,13 +1123,14 @@ static void stellaris_adc_write(void *opaque, hwaddr offset, s->sspri = value; break; case 0x28: /* PSSI */ - hw_error("Not implemented: ADC sample initiate\n"); + qemu_log_mask(LOG_UNIMP, "ADC: sample initiate unimplemented"); break; case 0x30: /* SAC */ s->sac = value; break; default: - hw_error("stellaris_adc_write: Bad offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "stellaris_adc: write at bad offset 0x%x\n", (int)offset); } stellaris_adc_update(s); } diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index bc4e66b862..64f52f80a5 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -30,6 +30,8 @@ #define ARM_PHYS_TIMER_PPI 30 #define ARM_VIRT_TIMER_PPI 27 +#define GEM_REVISION 0x40070106 + #define GIC_BASE_ADDR 0xf9000000 #define GIC_DIST_ADDR 0xf9010000 #define GIC_CPU_ADDR 0xf9020000 @@ -334,8 +336,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qemu_check_nic_model(nd, TYPE_CADENCE_GEM); qdev_set_nic_properties(DEVICE(&s->gem[i]), nd); } + object_property_set_int(OBJECT(&s->gem[i]), GEM_REVISION, "revision", + &error_abort); object_property_set_int(OBJECT(&s->gem[i]), 2, "num-priority-queues", - &error_abort); + &error_abort); object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -439,12 +443,6 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) dc->props = xlnx_zynqmp_props; dc->realize = xlnx_zynqmp_realize; - - /* - * Reason: creates an ARM CPU, thus use after free(), see - * arm_cpu_class_init() - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo xlnx_zynqmp_type_info = { diff --git a/hw/block/fdc.c b/hw/block/fdc.c index a328693d15..2e629b398b 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -2521,8 +2521,8 @@ static void fdctrl_result_timer(void *opaque) } /* Init functions */ -static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp, - DeviceState *fdc_dev) +static void fdctrl_connect_drives(FDCtrl *fdctrl, DeviceState *fdc_dev, + Error **errp) { unsigned int i; FDrive *drive; @@ -2675,7 +2675,7 @@ static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl, } floppy_bus_create(fdctrl, &fdctrl->bus, dev); - fdctrl_connect_drives(fdctrl, errp, dev); + fdctrl_connect_drives(fdctrl, dev, errp); } static const MemoryRegionPortio fdc_portio_list[] = { diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index b75f28d473..bff706ab3a 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -102,7 +102,7 @@ typedef struct Exynos4210UartReg { uint32_t reset_value; } Exynos4210UartReg; -static Exynos4210UartReg exynos4210_uart_regs[] = { +static const Exynos4210UartReg exynos4210_uart_regs[] = { {"ULCON", ULCON, 0x00000000}, {"UCON", UCON, 0x00003000}, {"UFCON", UFCON, 0x00000000}, @@ -220,7 +220,7 @@ static uint8_t fifo_retrieve(Exynos4210UartFIFO *q) return ret; } -static int fifo_elements_number(Exynos4210UartFIFO *q) +static int fifo_elements_number(const Exynos4210UartFIFO *q) { if (q->sp < q->rp) { return q->size - q->rp + q->sp; @@ -229,7 +229,7 @@ static int fifo_elements_number(Exynos4210UartFIFO *q) return q->sp - q->rp; } -static int fifo_empty_elements_number(Exynos4210UartFIFO *q) +static int fifo_empty_elements_number(const Exynos4210UartFIFO *q) { return q->size - fifo_elements_number(q); } @@ -245,7 +245,7 @@ static void fifo_reset(Exynos4210UartFIFO *q) q->rp = 0; } -static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s) +static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(const Exynos4210UartState *s) { uint32_t level = 0; uint32_t reg; diff --git a/hw/core/machine.c b/hw/core/machine.c index 0d92672203..ada9eea483 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -396,6 +396,11 @@ static void machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 128 * M_BYTE; mc->rom_file_has_mr = true; + /* numa node memory size aligned on 8MB by default. + * On Linux, each node's border has to be 8MB aligned + */ + mc->numa_mem_align_shift = 23; + object_class_property_add_str(oc, "accel", machine_get_accel, machine_set_accel, &error_abort); object_class_property_set_description(oc, "accel", diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index 27c8369b57..864832db34 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -40,6 +40,12 @@ static void machine_none_init(MachineState *mch) memory_region_allocate_system_memory(ram, NULL, "ram", mch->ram_size); memory_region_add_subregion(get_system_memory(), 0, ram); } + + if (mch->kernel_filename) { + error_report("The -kernel parameter is not supported " + "(use the generic 'loader' device instead)."); + exit(1); + } } static void machine_none_machine_init(MachineClass *mc) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index c34be1c1ba..79c2014135 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -124,8 +124,12 @@ static void release_drive(Object *obj, const char *name, void *opaque) BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); if (*ptr) { + AioContext *ctx = blk_get_aio_context(*ptr); + + aio_context_acquire(ctx); blockdev_auto_del(*ptr); blk_detach_dev(*ptr, dev); + aio_context_release(ctx); } } @@ -405,7 +409,7 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name, if (value) { ref = blk_name(value); if (!*ref) { - BlockDriverState *bs = blk_bs(value); + const BlockDriverState *bs = blk_bs(value); if (bs) { ref = bdrv_get_node_name(bs); } diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 6ab4265eb4..fa3617db2d 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1010,7 +1010,8 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) object_property_set_str(OBJECT(dev), value, name, &error_abort); } -void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value) +void qdev_prop_set_macaddr(DeviceState *dev, const char *name, + const uint8_t *value) { char str[2 * 6 + 5 + 1]; snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 1e7fb33246..02b632f6b3 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -39,9 +39,9 @@ #include "qapi-event.h" #include "migration/migration.h" -int qdev_hotplug = 0; +bool qdev_hotplug = false; static bool qdev_hot_added = false; -static bool qdev_hot_removed = false; +bool qdev_hot_removed = false; const VMStateDescription *qdev_get_vmsd(DeviceState *dev) { @@ -271,40 +271,6 @@ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) return hotplug_ctrl; } -void qdev_unplug(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - HotplugHandler *hotplug_ctrl; - HotplugHandlerClass *hdc; - - if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); - return; - } - - if (!dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, - object_get_typename(OBJECT(dev))); - return; - } - - qdev_hot_removed = true; - - hotplug_ctrl = qdev_get_hotplug_handler(dev); - /* hotpluggable device MUST have HotplugHandler, if it doesn't - * then something is very wrong with it */ - g_assert(hotplug_ctrl); - - /* If device supports async unplug just request it to be done, - * otherwise just remove it synchronously */ - hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); - if (hdc->unplug_request) { - hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); - } else { - hotplug_handler_unplug(hotplug_ctrl, dev, errp); - } -} - static int qdev_reset_one(DeviceState *dev, void *opaque) { device_reset(dev); @@ -385,7 +351,7 @@ void qdev_machine_creation_done(void) * ok, initial machine setup is done, starting from now we can * only create hotpluggable devices */ - qdev_hotplug = 1; + qdev_hotplug = true; } bool qdev_machine_modified(void) @@ -1037,13 +1003,6 @@ static bool device_get_hotplugged(Object *obj, Error **err) return dev->hotplugged; } -static void device_set_hotplugged(Object *obj, bool value, Error **err) -{ - DeviceState *dev = DEVICE(obj); - - dev->hotplugged = value; -} - static void device_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -1063,7 +1022,7 @@ static void device_initfn(Object *obj) object_property_add_bool(obj, "hotpluggable", device_get_hotpluggable, NULL, NULL); object_property_add_bool(obj, "hotplugged", - device_get_hotplugged, device_set_hotplugged, + device_get_hotplugged, NULL, &error_abort); class = object_get_class(OBJECT(dev)); diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 1174220394..03d9197f71 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" -#include "cpu.h" #include "qemu/error-report.h" #include "ui/console.h" #include "hw/sysbus.h" @@ -114,8 +113,8 @@ static void cg3_update_display(void *opaque) for (y = 0; y < height; y++) { int update = s->full_update; - page = (y * width) & TARGET_PAGE_MASK; - update |= memory_region_get_dirty(&s->vram_mem, page, page + width, + page = y * width; + update |= memory_region_get_dirty(&s->vram_mem, page, width, DIRTY_MEMORY_VGA); if (update) { if (y_start < 0) { @@ -148,8 +147,7 @@ static void cg3_update_display(void *opaque) } if (page_max >= page_min) { memory_region_reset_dirty(&s->vram_mem, - page_min, page_max - page_min + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); + page_min, page_max - page_min, DIRTY_MEMORY_VGA); } /* vsync interrupt? */ if (s->regs[0] & CG3_CR_ENABLE_INTS) { @@ -305,8 +303,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp) vmstate_register_ram_global(&s->rom); fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE); if (fcode_filename) { - ret = load_image_targphys(fcode_filename, s->prom_addr, - FCODE_MAX_ROM_SIZE); + ret = load_image_mr(fcode_filename, &s->rom); g_free(fcode_filename); if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { error_report("cg3: could not load prom '%s'", CG3_ROM_FILE); @@ -371,7 +368,6 @@ static Property cg3_properties[] = { DEFINE_PROP_UINT16("width", CG3State, width, -1), DEFINE_PROP_UINT16("height", CG3State, height, -1), DEFINE_PROP_UINT16("depth", CG3State, depth, -1), - DEFINE_PROP_UINT64("prom-addr", CG3State, prom_addr, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/cirrus_vga_rop2.h b/hw/display/cirrus_vga_rop2.h index b86bcd6e09..b208b7348a 100644 --- a/hw/display/cirrus_vga_rop2.h +++ b/hw/display/cirrus_vga_rop2.h @@ -29,8 +29,8 @@ #elif DEPTH == 24 #define PUTPIXEL(s, a, c) do { \ ROP_OP(s, a, c); \ - ROP_OP(s, a + 1, (col >> 8)); \ - ROP_OP(s, a + 2, (col >> 16)); \ + ROP_OP(s, a + 1, (c >> 8)); \ + ROP_OP(s, a + 2, (c >> 16)); \ } while (0) #elif DEPTH == 32 #define PUTPIXEL(s, a, c) ROP_OP_32(s, a, c) diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index e5be713406..fd0b2bec65 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1263,6 +1263,7 @@ static void exynos4210_fimd_update(void *opaque) Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; DisplaySurface *surface; Exynos4210fimdWindow *w; + DirtyBitmapSnapshot *snap; int i, line; hwaddr fb_line_addr, inc_size; int scrn_height; @@ -1291,10 +1292,12 @@ static void exynos4210_fimd_update(void *opaque) memory_region_sync_dirty_bitmap(w->mem_section.mr); host_fb_addr = w->host_fb_addr; fb_line_addr = w->mem_section.offset_within_region; + snap = memory_region_snapshot_and_clear_dirty(w->mem_section.mr, + fb_line_addr, inc_size * scrn_height, DIRTY_MEMORY_VGA); for (line = 0; line < scrn_height; line++) { - is_dirty = memory_region_get_dirty(w->mem_section.mr, - fb_line_addr, scrn_width, DIRTY_MEMORY_VGA); + is_dirty = memory_region_snapshot_get_dirty(w->mem_section.mr, + snap, fb_line_addr, scrn_width); if (s->invalidate || is_dirty) { if (first_line == -1) { @@ -1309,9 +1312,7 @@ static void exynos4210_fimd_update(void *opaque) fb_line_addr += inc_size; is_dirty = false; } - memory_region_reset_dirty(w->mem_section.mr, - w->mem_section.offset_within_region, - w->fb_len, DIRTY_MEMORY_VGA); + g_free(snap); blend = true; } } diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c index 25aa46c8c7..d7310d25f2 100644 --- a/hw/display/framebuffer.c +++ b/hw/display/framebuffer.c @@ -67,7 +67,7 @@ void framebuffer_update_display( int *first_row, /* Input and output. */ int *last_row /* Output only */) { - hwaddr src_len; + DirtyBitmapSnapshot *snap; uint8_t *dest; uint8_t *src; int first, last = 0; @@ -78,7 +78,6 @@ void framebuffer_update_display( i = *first_row; *first_row = -1; - src_len = (hwaddr)src_width * rows; mem = mem_section->mr; if (!mem) { @@ -102,9 +101,10 @@ void framebuffer_update_display( src += i * src_width; dest += i * dest_row_pitch; + snap = memory_region_snapshot_and_clear_dirty(mem, addr, src_width * rows, + DIRTY_MEMORY_VGA); for (; i < rows; i++) { - dirty = memory_region_get_dirty(mem, addr, src_width, - DIRTY_MEMORY_VGA); + dirty = memory_region_snapshot_get_dirty(mem, snap, addr, src_width); if (dirty || invalidate) { fn(opaque, dest, src, cols, dest_col_pitch); if (first == -1) @@ -115,11 +115,10 @@ void framebuffer_update_display( src += src_width; dest += dest_row_pitch; } + g_free(snap); if (first < 0) { return; } - memory_region_reset_dirty(mem, mem_section->offset_within_region, src_len, - DIRTY_MEMORY_VGA); *first_row = first; *last_row = last; } diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 8cdc205dd9..86557d14a9 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -64,17 +64,8 @@ typedef struct G364State { static inline int check_dirty(G364State *s, ram_addr_t page) { - return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE, - DIRTY_MEMORY_VGA); -} - -static inline void reset_dirty(G364State *s, - ram_addr_t page_min, ram_addr_t page_max) -{ - memory_region_reset_dirty(&s->mem_vram, - page_min, - page_max + G364_PAGE_SIZE - page_min - 1, - DIRTY_MEMORY_VGA); + return memory_region_test_and_clear_dirty(&s->mem_vram, page, G364_PAGE_SIZE, + DIRTY_MEMORY_VGA); } static void g364fb_draw_graphic8(G364State *s) @@ -83,7 +74,7 @@ static void g364fb_draw_graphic8(G364State *s) int i, w; uint8_t *vram; uint8_t *data_display, *dd; - ram_addr_t page, page_min, page_max; + ram_addr_t page; int x, y; int xmin, xmax; int ymin, ymax; @@ -114,8 +105,6 @@ static void g364fb_draw_graphic8(G364State *s) } page = 0; - page_min = (ram_addr_t)-1; - page_max = 0; x = y = 0; xmin = s->width; @@ -137,9 +126,6 @@ static void g364fb_draw_graphic8(G364State *s) if (check_dirty(s, page)) { if (y < ymin) ymin = ymax = y; - if (page_min == (ram_addr_t)-1) - page_min = page; - page_max = page; if (x < xmin) xmin = x; for (i = 0; i < G364_PAGE_SIZE; i++) { @@ -196,10 +182,7 @@ static void g364fb_draw_graphic8(G364State *s) ymax = y; } else { int dy; - if (page_min != (ram_addr_t)-1) { - reset_dirty(s, page_min, page_max); - page_min = (ram_addr_t)-1; - page_max = 0; + if (xmax || ymax) { dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); xmin = s->width; @@ -219,9 +202,8 @@ static void g364fb_draw_graphic8(G364State *s) } done: - if (page_min != (ram_addr_t)-1) { + if (xmax || ymax) { dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); - reset_dirty(s, page_min, page_max); } } diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 0d02f0efe6..4d94cecd72 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -26,6 +26,7 @@ #include "qemu/queue.h" #include "qemu/atomic.h" #include "sysemu/sysemu.h" +#include "migration/migration.h" #include "trace.h" #include "qxl.h" @@ -304,6 +305,16 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl) qxl->ssd.cursor = cursor_builtin_hidden(); } +static uint32_t qxl_crc32(const uint8_t *p, unsigned len) +{ + /* + * zlib xors the seed with 0xffffffff, and xors the result + * again with 0xffffffff; Both are not done with linux's crc32, + * which we want to be compatible with, so undo that. + */ + return crc32(0xffffffff, p, len) ^ 0xffffffff; +} + static ram_addr_t qxl_rom_size(void) { #define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes)) @@ -368,6 +379,18 @@ static void init_qxl_rom(PCIQXLDevice *d) rom->num_pages = cpu_to_le32(num_pages); rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size); + if (d->xres && d->yres) { + /* needs linux kernel 4.12+ to work */ + rom->client_monitors_config.count = 1; + rom->client_monitors_config.heads[0].left = 0; + rom->client_monitors_config.heads[0].top = 0; + rom->client_monitors_config.heads[0].right = cpu_to_le32(d->xres); + rom->client_monitors_config.heads[0].bottom = cpu_to_le32(d->yres); + rom->client_monitors_config_crc = qxl_crc32( + (const uint8_t *)&rom->client_monitors_config, + sizeof(rom->client_monitors_config)); + } + d->shadow_rom = *rom; d->rom = rom; d->modes = modes; @@ -639,6 +662,30 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) qxl->guest_primary.commands++; qxl_track_command(qxl, ext); qxl_log_command(qxl, "cmd", ext); + { + /* + * Windows 8 drivers place qxl commands in the vram + * (instead of the ram) bar. We can't live migrate such a + * guest, so add a migration blocker in case we detect + * this, to avoid triggering the assert in pre_save(). + * + * https://cgit.freedesktop.org/spice/win32/qxl-wddm-dod/commit/?id=f6e099db39e7d0787f294d5fd0dce328b5210faa + */ + void *msg = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + if (msg != NULL && ( + msg < (void *)qxl->vga.vram_ptr || + msg > ((void *)qxl->vga.vram_ptr + qxl->vga.vram_size))) { + if (!qxl->migration_blocker) { + Error *local_err = NULL; + error_setg(&qxl->migration_blocker, + "qxl: guest bug: command not in ram bar"); + migrate_add_blocker(qxl->migration_blocker, &local_err); + if (local_err) { + error_report_err(local_err); + } + } + } + } trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); return true; default: @@ -986,16 +1033,6 @@ static void interface_set_client_capabilities(QXLInstance *sin, qxl_send_events(qxl, QXL_INTERRUPT_CLIENT); } -static uint32_t qxl_crc32(const uint8_t *p, unsigned len) -{ - /* - * zlib xors the seed with 0xffffffff, and xors the result - * again with 0xffffffff; Both are not done with linux's crc32, - * which we want to be compatible with, so undo that. - */ - return crc32(0xffffffff, p, len) ^ 0xffffffff; -} - static bool qxl_rom_monitors_config_changed(QXLRom *rom, VDAgentMonitorsConfig *monitors_config, unsigned int max_outputs) @@ -1146,6 +1183,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d) update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_DEFAULT); qemu_spice_create_host_primary(&d->ssd); d->mode = QXL_MODE_VGA; + qemu_spice_display_switch(&d->ssd, d->ssd.ds); vga_dirty_log_start(&d->vga); graphic_hw_update(d->vga.con); } @@ -1235,6 +1273,12 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) qemu_spice_create_host_memslot(&d->ssd); qxl_soft_reset(d); + if (d->migration_blocker) { + migrate_del_blocker(d->migration_blocker); + error_free(d->migration_blocker); + d->migration_blocker = NULL; + } + if (startstop) { qemu_spice_display_start(); } @@ -2365,6 +2409,8 @@ static Property qxl_properties[] = { #if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0), #endif + DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), + DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/qxl.h b/hw/display/qxl.h index d2d49dd933..f6556adb73 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -40,6 +40,7 @@ typedef struct PCIQXLDevice { uint32_t cmdlog; uint32_t guest_bug; + Error *migration_blocker; enum qxl_mode mode; uint32_t cmdflags; @@ -118,6 +119,8 @@ typedef struct PCIQXLDevice { uint32_t vram_size_mb; uint32_t vram32_size_mb; uint32_t vgamem_size_mb; + uint32_t xres; + uint32_t yres; /* qxl_render_update state */ int render_update_cookie_num; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 040a0b93f2..2094adbc9c 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -2,6 +2,7 @@ * QEMU SM501 Device * * Copyright (c) 2008 Shin-ichiro KAWASAKI + * Copyright (c) 2016 BALATON Zoltan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -31,6 +33,7 @@ #include "ui/console.h" #include "hw/devices.h" #include "hw/sysbus.h" +#include "hw/pci/pci.h" #include "qemu/range.h" #include "ui/pixel_ops.h" #include "exec/address-spaces.h" @@ -38,10 +41,13 @@ /* * Status: 2010/05/07 * - Minimum implementation for Linux console : mmio regs and CRT layer. - * - 2D grapihcs acceleration partially supported : only fill rectangle. + * - 2D graphics acceleration partially supported : only fill rectangle. * - * TODO: + * Status: 2016/12/04 + * - Misc fixes: endianness, hardware cursor * - Panel support + * + * TODO: * - Touch panel support * - USB support * - UART support @@ -49,395 +55,396 @@ * - Performance tuning */ -//#define DEBUG_SM501 -//#define DEBUG_BITBLT +/*#define DEBUG_SM501*/ +/*#define DEBUG_BITBLT*/ #ifdef DEBUG_SM501 #define SM501_DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) #else -#define SM501_DPRINTF(fmt, ...) do {} while(0) +#define SM501_DPRINTF(fmt, ...) do {} while (0) #endif - #define MMIO_BASE_OFFSET 0x3e00000 +#define MMIO_SIZE 0x200000 +#define DC_PALETTE_ENTRIES (0x400 * 3) /* SM501 register definitions taken from "linux/include/linux/sm501-regs.h" */ /* System Configuration area */ /* System config base */ -#define SM501_SYS_CONFIG (0x000000) +#define SM501_SYS_CONFIG (0x000000) /* config 1 */ -#define SM501_SYSTEM_CONTROL (0x000000) +#define SM501_SYSTEM_CONTROL (0x000000) -#define SM501_SYSCTRL_PANEL_TRISTATE (1<<0) -#define SM501_SYSCTRL_MEM_TRISTATE (1<<1) -#define SM501_SYSCTRL_CRT_TRISTATE (1<<2) +#define SM501_SYSCTRL_PANEL_TRISTATE (1 << 0) +#define SM501_SYSCTRL_MEM_TRISTATE (1 << 1) +#define SM501_SYSCTRL_CRT_TRISTATE (1 << 2) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3<<4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2 << 4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3 << 4) -#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1<<6) -#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1<<7) -#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1<<11) -#define SM501_SYSCTRL_PCI_BURST_READ_EN (1<<15) +#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1 << 6) +#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1 << 7) +#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1 << 11) +#define SM501_SYSCTRL_PCI_BURST_READ_EN (1 << 15) /* miscellaneous control */ -#define SM501_MISC_CONTROL (0x000004) +#define SM501_MISC_CONTROL (0x000004) -#define SM501_MISC_BUS_SH (0x0) -#define SM501_MISC_BUS_PCI (0x1) -#define SM501_MISC_BUS_XSCALE (0x2) -#define SM501_MISC_BUS_NEC (0x6) -#define SM501_MISC_BUS_MASK (0x7) +#define SM501_MISC_BUS_SH (0x0) +#define SM501_MISC_BUS_PCI (0x1) +#define SM501_MISC_BUS_XSCALE (0x2) +#define SM501_MISC_BUS_NEC (0x6) +#define SM501_MISC_BUS_MASK (0x7) -#define SM501_MISC_VR_62MB (1<<3) -#define SM501_MISC_CDR_RESET (1<<7) -#define SM501_MISC_USB_LB (1<<8) -#define SM501_MISC_USB_SLAVE (1<<9) -#define SM501_MISC_BL_1 (1<<10) -#define SM501_MISC_MC (1<<11) -#define SM501_MISC_DAC_POWER (1<<12) -#define SM501_MISC_IRQ_INVERT (1<<16) -#define SM501_MISC_SH (1<<17) +#define SM501_MISC_VR_62MB (1 << 3) +#define SM501_MISC_CDR_RESET (1 << 7) +#define SM501_MISC_USB_LB (1 << 8) +#define SM501_MISC_USB_SLAVE (1 << 9) +#define SM501_MISC_BL_1 (1 << 10) +#define SM501_MISC_MC (1 << 11) +#define SM501_MISC_DAC_POWER (1 << 12) +#define SM501_MISC_IRQ_INVERT (1 << 16) +#define SM501_MISC_SH (1 << 17) -#define SM501_MISC_HOLD_EMPTY (0<<18) -#define SM501_MISC_HOLD_8 (1<<18) -#define SM501_MISC_HOLD_16 (2<<18) -#define SM501_MISC_HOLD_24 (3<<18) -#define SM501_MISC_HOLD_32 (4<<18) -#define SM501_MISC_HOLD_MASK (7<<18) +#define SM501_MISC_HOLD_EMPTY (0 << 18) +#define SM501_MISC_HOLD_8 (1 << 18) +#define SM501_MISC_HOLD_16 (2 << 18) +#define SM501_MISC_HOLD_24 (3 << 18) +#define SM501_MISC_HOLD_32 (4 << 18) +#define SM501_MISC_HOLD_MASK (7 << 18) -#define SM501_MISC_FREQ_12 (1<<24) -#define SM501_MISC_PNL_24BIT (1<<25) -#define SM501_MISC_8051_LE (1<<26) +#define SM501_MISC_FREQ_12 (1 << 24) +#define SM501_MISC_PNL_24BIT (1 << 25) +#define SM501_MISC_8051_LE (1 << 26) -#define SM501_GPIO31_0_CONTROL (0x000008) -#define SM501_GPIO63_32_CONTROL (0x00000C) -#define SM501_DRAM_CONTROL (0x000010) +#define SM501_GPIO31_0_CONTROL (0x000008) +#define SM501_GPIO63_32_CONTROL (0x00000C) +#define SM501_DRAM_CONTROL (0x000010) /* command list */ -#define SM501_ARBTRTN_CONTROL (0x000014) +#define SM501_ARBTRTN_CONTROL (0x000014) /* command list */ -#define SM501_COMMAND_LIST_STATUS (0x000024) +#define SM501_COMMAND_LIST_STATUS (0x000024) /* interrupt debug */ -#define SM501_RAW_IRQ_STATUS (0x000028) -#define SM501_RAW_IRQ_CLEAR (0x000028) -#define SM501_IRQ_STATUS (0x00002C) -#define SM501_IRQ_MASK (0x000030) -#define SM501_DEBUG_CONTROL (0x000034) +#define SM501_RAW_IRQ_STATUS (0x000028) +#define SM501_RAW_IRQ_CLEAR (0x000028) +#define SM501_IRQ_STATUS (0x00002C) +#define SM501_IRQ_MASK (0x000030) +#define SM501_DEBUG_CONTROL (0x000034) /* power management */ -#define SM501_POWERMODE_P2X_SRC (1<<29) -#define SM501_POWERMODE_V2X_SRC (1<<20) -#define SM501_POWERMODE_M_SRC (1<<12) -#define SM501_POWERMODE_M1_SRC (1<<4) - -#define SM501_CURRENT_GATE (0x000038) -#define SM501_CURRENT_CLOCK (0x00003C) -#define SM501_POWER_MODE_0_GATE (0x000040) -#define SM501_POWER_MODE_0_CLOCK (0x000044) -#define SM501_POWER_MODE_1_GATE (0x000048) -#define SM501_POWER_MODE_1_CLOCK (0x00004C) -#define SM501_SLEEP_MODE_GATE (0x000050) -#define SM501_POWER_MODE_CONTROL (0x000054) +#define SM501_POWERMODE_P2X_SRC (1 << 29) +#define SM501_POWERMODE_V2X_SRC (1 << 20) +#define SM501_POWERMODE_M_SRC (1 << 12) +#define SM501_POWERMODE_M1_SRC (1 << 4) + +#define SM501_CURRENT_GATE (0x000038) +#define SM501_CURRENT_CLOCK (0x00003C) +#define SM501_POWER_MODE_0_GATE (0x000040) +#define SM501_POWER_MODE_0_CLOCK (0x000044) +#define SM501_POWER_MODE_1_GATE (0x000048) +#define SM501_POWER_MODE_1_CLOCK (0x00004C) +#define SM501_SLEEP_MODE_GATE (0x000050) +#define SM501_POWER_MODE_CONTROL (0x000054) /* power gates for units within the 501 */ -#define SM501_GATE_HOST (0) -#define SM501_GATE_MEMORY (1) -#define SM501_GATE_DISPLAY (2) -#define SM501_GATE_2D_ENGINE (3) -#define SM501_GATE_CSC (4) -#define SM501_GATE_ZVPORT (5) -#define SM501_GATE_GPIO (6) -#define SM501_GATE_UART0 (7) -#define SM501_GATE_UART1 (8) -#define SM501_GATE_SSP (10) -#define SM501_GATE_USB_HOST (11) -#define SM501_GATE_USB_GADGET (12) -#define SM501_GATE_UCONTROLLER (17) -#define SM501_GATE_AC97 (18) +#define SM501_GATE_HOST (0) +#define SM501_GATE_MEMORY (1) +#define SM501_GATE_DISPLAY (2) +#define SM501_GATE_2D_ENGINE (3) +#define SM501_GATE_CSC (4) +#define SM501_GATE_ZVPORT (5) +#define SM501_GATE_GPIO (6) +#define SM501_GATE_UART0 (7) +#define SM501_GATE_UART1 (8) +#define SM501_GATE_SSP (10) +#define SM501_GATE_USB_HOST (11) +#define SM501_GATE_USB_GADGET (12) +#define SM501_GATE_UCONTROLLER (17) +#define SM501_GATE_AC97 (18) /* panel clock */ -#define SM501_CLOCK_P2XCLK (24) +#define SM501_CLOCK_P2XCLK (24) /* crt clock */ -#define SM501_CLOCK_V2XCLK (16) +#define SM501_CLOCK_V2XCLK (16) /* main clock */ -#define SM501_CLOCK_MCLK (8) +#define SM501_CLOCK_MCLK (8) /* SDRAM controller clock */ -#define SM501_CLOCK_M1XCLK (0) +#define SM501_CLOCK_M1XCLK (0) /* config 2 */ -#define SM501_PCI_MASTER_BASE (0x000058) -#define SM501_ENDIAN_CONTROL (0x00005C) -#define SM501_DEVICEID (0x000060) +#define SM501_PCI_MASTER_BASE (0x000058) +#define SM501_ENDIAN_CONTROL (0x00005C) +#define SM501_DEVICEID (0x000060) /* 0x050100A0 */ -#define SM501_DEVICEID_SM501 (0x05010000) -#define SM501_DEVICEID_IDMASK (0xffff0000) -#define SM501_DEVICEID_REVMASK (0x000000ff) +#define SM501_DEVICEID_SM501 (0x05010000) +#define SM501_DEVICEID_IDMASK (0xffff0000) +#define SM501_DEVICEID_REVMASK (0x000000ff) -#define SM501_PLLCLOCK_COUNT (0x000064) -#define SM501_MISC_TIMING (0x000068) -#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) +#define SM501_PLLCLOCK_COUNT (0x000064) +#define SM501_MISC_TIMING (0x000068) +#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) -#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) +#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) /* GPIO base */ -#define SM501_GPIO (0x010000) -#define SM501_GPIO_DATA_LOW (0x00) -#define SM501_GPIO_DATA_HIGH (0x04) -#define SM501_GPIO_DDR_LOW (0x08) -#define SM501_GPIO_DDR_HIGH (0x0C) -#define SM501_GPIO_IRQ_SETUP (0x10) -#define SM501_GPIO_IRQ_STATUS (0x14) -#define SM501_GPIO_IRQ_RESET (0x14) +#define SM501_GPIO (0x010000) +#define SM501_GPIO_DATA_LOW (0x00) +#define SM501_GPIO_DATA_HIGH (0x04) +#define SM501_GPIO_DDR_LOW (0x08) +#define SM501_GPIO_DDR_HIGH (0x0C) +#define SM501_GPIO_IRQ_SETUP (0x10) +#define SM501_GPIO_IRQ_STATUS (0x14) +#define SM501_GPIO_IRQ_RESET (0x14) /* I2C controller base */ -#define SM501_I2C (0x010040) -#define SM501_I2C_BYTE_COUNT (0x00) -#define SM501_I2C_CONTROL (0x01) -#define SM501_I2C_STATUS (0x02) -#define SM501_I2C_RESET (0x02) -#define SM501_I2C_SLAVE_ADDRESS (0x03) -#define SM501_I2C_DATA (0x04) +#define SM501_I2C (0x010040) +#define SM501_I2C_BYTE_COUNT (0x00) +#define SM501_I2C_CONTROL (0x01) +#define SM501_I2C_STATUS (0x02) +#define SM501_I2C_RESET (0x02) +#define SM501_I2C_SLAVE_ADDRESS (0x03) +#define SM501_I2C_DATA (0x04) /* SSP base */ -#define SM501_SSP (0x020000) +#define SM501_SSP (0x020000) /* Uart 0 base */ -#define SM501_UART0 (0x030000) +#define SM501_UART0 (0x030000) /* Uart 1 base */ -#define SM501_UART1 (0x030020) +#define SM501_UART1 (0x030020) /* USB host port base */ -#define SM501_USB_HOST (0x040000) +#define SM501_USB_HOST (0x040000) /* USB slave/gadget base */ -#define SM501_USB_GADGET (0x060000) +#define SM501_USB_GADGET (0x060000) /* USB slave/gadget data port base */ -#define SM501_USB_GADGET_DATA (0x070000) +#define SM501_USB_GADGET_DATA (0x070000) /* Display controller/video engine base */ -#define SM501_DC (0x080000) +#define SM501_DC (0x080000) /* common defines for the SM501 address registers */ -#define SM501_ADDR_FLIP (1<<31) -#define SM501_ADDR_EXT (1<<27) -#define SM501_ADDR_CS1 (1<<26) -#define SM501_ADDR_MASK (0x3f << 26) +#define SM501_ADDR_FLIP (1 << 31) +#define SM501_ADDR_EXT (1 << 27) +#define SM501_ADDR_CS1 (1 << 26) +#define SM501_ADDR_MASK (0x3f << 26) -#define SM501_FIFO_MASK (0x3 << 16) -#define SM501_FIFO_1 (0x0 << 16) -#define SM501_FIFO_3 (0x1 << 16) -#define SM501_FIFO_7 (0x2 << 16) -#define SM501_FIFO_11 (0x3 << 16) +#define SM501_FIFO_MASK (0x3 << 16) +#define SM501_FIFO_1 (0x0 << 16) +#define SM501_FIFO_3 (0x1 << 16) +#define SM501_FIFO_7 (0x2 << 16) +#define SM501_FIFO_11 (0x3 << 16) /* common registers for panel and the crt */ -#define SM501_OFF_DC_H_TOT (0x000) -#define SM501_OFF_DC_V_TOT (0x008) -#define SM501_OFF_DC_H_SYNC (0x004) -#define SM501_OFF_DC_V_SYNC (0x00C) - -#define SM501_DC_PANEL_CONTROL (0x000) - -#define SM501_DC_PANEL_CONTROL_FPEN (1<<27) -#define SM501_DC_PANEL_CONTROL_BIAS (1<<26) -#define SM501_DC_PANEL_CONTROL_DATA (1<<25) -#define SM501_DC_PANEL_CONTROL_VDD (1<<24) -#define SM501_DC_PANEL_CONTROL_DP (1<<23) - -#define SM501_DC_PANEL_CONTROL_TFT_888 (0<<21) -#define SM501_DC_PANEL_CONTROL_TFT_333 (1<<21) -#define SM501_DC_PANEL_CONTROL_TFT_444 (2<<21) - -#define SM501_DC_PANEL_CONTROL_DE (1<<20) - -#define SM501_DC_PANEL_CONTROL_LCD_TFT (0<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2<<18) - -#define SM501_DC_PANEL_CONTROL_CP (1<<14) -#define SM501_DC_PANEL_CONTROL_VSP (1<<13) -#define SM501_DC_PANEL_CONTROL_HSP (1<<12) -#define SM501_DC_PANEL_CONTROL_CK (1<<9) -#define SM501_DC_PANEL_CONTROL_TE (1<<8) -#define SM501_DC_PANEL_CONTROL_VPD (1<<7) -#define SM501_DC_PANEL_CONTROL_VP (1<<6) -#define SM501_DC_PANEL_CONTROL_HPD (1<<5) -#define SM501_DC_PANEL_CONTROL_HP (1<<4) -#define SM501_DC_PANEL_CONTROL_GAMMA (1<<3) -#define SM501_DC_PANEL_CONTROL_EN (1<<2) - -#define SM501_DC_PANEL_CONTROL_8BPP (0<<0) -#define SM501_DC_PANEL_CONTROL_16BPP (1<<0) -#define SM501_DC_PANEL_CONTROL_32BPP (2<<0) - - -#define SM501_DC_PANEL_PANNING_CONTROL (0x004) -#define SM501_DC_PANEL_COLOR_KEY (0x008) -#define SM501_DC_PANEL_FB_ADDR (0x00C) -#define SM501_DC_PANEL_FB_OFFSET (0x010) -#define SM501_DC_PANEL_FB_WIDTH (0x014) -#define SM501_DC_PANEL_FB_HEIGHT (0x018) -#define SM501_DC_PANEL_TL_LOC (0x01C) -#define SM501_DC_PANEL_BR_LOC (0x020) -#define SM501_DC_PANEL_H_TOT (0x024) -#define SM501_DC_PANEL_H_SYNC (0x028) -#define SM501_DC_PANEL_V_TOT (0x02C) -#define SM501_DC_PANEL_V_SYNC (0x030) -#define SM501_DC_PANEL_CUR_LINE (0x034) - -#define SM501_DC_VIDEO_CONTROL (0x040) -#define SM501_DC_VIDEO_FB0_ADDR (0x044) -#define SM501_DC_VIDEO_FB_WIDTH (0x048) -#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) -#define SM501_DC_VIDEO_TL_LOC (0x050) -#define SM501_DC_VIDEO_BR_LOC (0x054) -#define SM501_DC_VIDEO_SCALE (0x058) -#define SM501_DC_VIDEO_INIT_SCALE (0x05C) -#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) -#define SM501_DC_VIDEO_FB1_ADDR (0x064) -#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) - -#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) -#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) -#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) -#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) -#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) -#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) -#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) -#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) -#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) -#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) - -#define SM501_DC_PANEL_HWC_BASE (0x0F0) -#define SM501_DC_PANEL_HWC_ADDR (0x0F0) -#define SM501_DC_PANEL_HWC_LOC (0x0F4) -#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) -#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) - -#define SM501_HWC_EN (1<<31) - -#define SM501_OFF_HWC_ADDR (0x00) -#define SM501_OFF_HWC_LOC (0x04) -#define SM501_OFF_HWC_COLOR_1_2 (0x08) -#define SM501_OFF_HWC_COLOR_3 (0x0C) - -#define SM501_DC_ALPHA_CONTROL (0x100) -#define SM501_DC_ALPHA_FB_ADDR (0x104) -#define SM501_DC_ALPHA_FB_OFFSET (0x108) -#define SM501_DC_ALPHA_TL_LOC (0x10C) -#define SM501_DC_ALPHA_BR_LOC (0x110) -#define SM501_DC_ALPHA_CHROMA_KEY (0x114) -#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) - -#define SM501_DC_CRT_CONTROL (0x200) - -#define SM501_DC_CRT_CONTROL_TVP (1<<15) -#define SM501_DC_CRT_CONTROL_CP (1<<14) -#define SM501_DC_CRT_CONTROL_VSP (1<<13) -#define SM501_DC_CRT_CONTROL_HSP (1<<12) -#define SM501_DC_CRT_CONTROL_VS (1<<11) -#define SM501_DC_CRT_CONTROL_BLANK (1<<10) -#define SM501_DC_CRT_CONTROL_SEL (1<<9) -#define SM501_DC_CRT_CONTROL_TE (1<<8) +#define SM501_OFF_DC_H_TOT (0x000) +#define SM501_OFF_DC_V_TOT (0x008) +#define SM501_OFF_DC_H_SYNC (0x004) +#define SM501_OFF_DC_V_SYNC (0x00C) + +#define SM501_DC_PANEL_CONTROL (0x000) + +#define SM501_DC_PANEL_CONTROL_FPEN (1 << 27) +#define SM501_DC_PANEL_CONTROL_BIAS (1 << 26) +#define SM501_DC_PANEL_CONTROL_DATA (1 << 25) +#define SM501_DC_PANEL_CONTROL_VDD (1 << 24) +#define SM501_DC_PANEL_CONTROL_DP (1 << 23) + +#define SM501_DC_PANEL_CONTROL_TFT_888 (0 << 21) +#define SM501_DC_PANEL_CONTROL_TFT_333 (1 << 21) +#define SM501_DC_PANEL_CONTROL_TFT_444 (2 << 21) + +#define SM501_DC_PANEL_CONTROL_DE (1 << 20) + +#define SM501_DC_PANEL_CONTROL_LCD_TFT (0 << 18) +#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1 << 18) +#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2 << 18) + +#define SM501_DC_PANEL_CONTROL_CP (1 << 14) +#define SM501_DC_PANEL_CONTROL_VSP (1 << 13) +#define SM501_DC_PANEL_CONTROL_HSP (1 << 12) +#define SM501_DC_PANEL_CONTROL_CK (1 << 9) +#define SM501_DC_PANEL_CONTROL_TE (1 << 8) +#define SM501_DC_PANEL_CONTROL_VPD (1 << 7) +#define SM501_DC_PANEL_CONTROL_VP (1 << 6) +#define SM501_DC_PANEL_CONTROL_HPD (1 << 5) +#define SM501_DC_PANEL_CONTROL_HP (1 << 4) +#define SM501_DC_PANEL_CONTROL_GAMMA (1 << 3) +#define SM501_DC_PANEL_CONTROL_EN (1 << 2) + +#define SM501_DC_PANEL_CONTROL_8BPP (0 << 0) +#define SM501_DC_PANEL_CONTROL_16BPP (1 << 0) +#define SM501_DC_PANEL_CONTROL_32BPP (2 << 0) + + +#define SM501_DC_PANEL_PANNING_CONTROL (0x004) +#define SM501_DC_PANEL_COLOR_KEY (0x008) +#define SM501_DC_PANEL_FB_ADDR (0x00C) +#define SM501_DC_PANEL_FB_OFFSET (0x010) +#define SM501_DC_PANEL_FB_WIDTH (0x014) +#define SM501_DC_PANEL_FB_HEIGHT (0x018) +#define SM501_DC_PANEL_TL_LOC (0x01C) +#define SM501_DC_PANEL_BR_LOC (0x020) +#define SM501_DC_PANEL_H_TOT (0x024) +#define SM501_DC_PANEL_H_SYNC (0x028) +#define SM501_DC_PANEL_V_TOT (0x02C) +#define SM501_DC_PANEL_V_SYNC (0x030) +#define SM501_DC_PANEL_CUR_LINE (0x034) + +#define SM501_DC_VIDEO_CONTROL (0x040) +#define SM501_DC_VIDEO_FB0_ADDR (0x044) +#define SM501_DC_VIDEO_FB_WIDTH (0x048) +#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) +#define SM501_DC_VIDEO_TL_LOC (0x050) +#define SM501_DC_VIDEO_BR_LOC (0x054) +#define SM501_DC_VIDEO_SCALE (0x058) +#define SM501_DC_VIDEO_INIT_SCALE (0x05C) +#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) +#define SM501_DC_VIDEO_FB1_ADDR (0x064) +#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) + +#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) +#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) +#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) +#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) +#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) +#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) +#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) +#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) +#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) +#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) + +#define SM501_DC_PANEL_HWC_BASE (0x0F0) +#define SM501_DC_PANEL_HWC_ADDR (0x0F0) +#define SM501_DC_PANEL_HWC_LOC (0x0F4) +#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) +#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) + +#define SM501_HWC_EN (1 << 31) + +#define SM501_OFF_HWC_ADDR (0x00) +#define SM501_OFF_HWC_LOC (0x04) +#define SM501_OFF_HWC_COLOR_1_2 (0x08) +#define SM501_OFF_HWC_COLOR_3 (0x0C) + +#define SM501_DC_ALPHA_CONTROL (0x100) +#define SM501_DC_ALPHA_FB_ADDR (0x104) +#define SM501_DC_ALPHA_FB_OFFSET (0x108) +#define SM501_DC_ALPHA_TL_LOC (0x10C) +#define SM501_DC_ALPHA_BR_LOC (0x110) +#define SM501_DC_ALPHA_CHROMA_KEY (0x114) +#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) + +#define SM501_DC_CRT_CONTROL (0x200) + +#define SM501_DC_CRT_CONTROL_TVP (1 << 15) +#define SM501_DC_CRT_CONTROL_CP (1 << 14) +#define SM501_DC_CRT_CONTROL_VSP (1 << 13) +#define SM501_DC_CRT_CONTROL_HSP (1 << 12) +#define SM501_DC_CRT_CONTROL_VS (1 << 11) +#define SM501_DC_CRT_CONTROL_BLANK (1 << 10) +#define SM501_DC_CRT_CONTROL_SEL (1 << 9) +#define SM501_DC_CRT_CONTROL_TE (1 << 8) #define SM501_DC_CRT_CONTROL_PIXEL_MASK (0xF << 4) -#define SM501_DC_CRT_CONTROL_GAMMA (1<<3) -#define SM501_DC_CRT_CONTROL_ENABLE (1<<2) +#define SM501_DC_CRT_CONTROL_GAMMA (1 << 3) +#define SM501_DC_CRT_CONTROL_ENABLE (1 << 2) -#define SM501_DC_CRT_CONTROL_8BPP (0<<0) -#define SM501_DC_CRT_CONTROL_16BPP (1<<0) -#define SM501_DC_CRT_CONTROL_32BPP (2<<0) +#define SM501_DC_CRT_CONTROL_8BPP (0 << 0) +#define SM501_DC_CRT_CONTROL_16BPP (1 << 0) +#define SM501_DC_CRT_CONTROL_32BPP (2 << 0) -#define SM501_DC_CRT_FB_ADDR (0x204) -#define SM501_DC_CRT_FB_OFFSET (0x208) -#define SM501_DC_CRT_H_TOT (0x20C) -#define SM501_DC_CRT_H_SYNC (0x210) -#define SM501_DC_CRT_V_TOT (0x214) -#define SM501_DC_CRT_V_SYNC (0x218) -#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) -#define SM501_DC_CRT_CUR_LINE (0x220) -#define SM501_DC_CRT_MONITOR_DETECT (0x224) +#define SM501_DC_CRT_FB_ADDR (0x204) +#define SM501_DC_CRT_FB_OFFSET (0x208) +#define SM501_DC_CRT_H_TOT (0x20C) +#define SM501_DC_CRT_H_SYNC (0x210) +#define SM501_DC_CRT_V_TOT (0x214) +#define SM501_DC_CRT_V_SYNC (0x218) +#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) +#define SM501_DC_CRT_CUR_LINE (0x220) +#define SM501_DC_CRT_MONITOR_DETECT (0x224) -#define SM501_DC_CRT_HWC_BASE (0x230) -#define SM501_DC_CRT_HWC_ADDR (0x230) -#define SM501_DC_CRT_HWC_LOC (0x234) -#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) -#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) +#define SM501_DC_CRT_HWC_BASE (0x230) +#define SM501_DC_CRT_HWC_ADDR (0x230) +#define SM501_DC_CRT_HWC_LOC (0x234) +#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) +#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) -#define SM501_DC_PANEL_PALETTE (0x400) +#define SM501_DC_PANEL_PALETTE (0x400) -#define SM501_DC_VIDEO_PALETTE (0x800) +#define SM501_DC_VIDEO_PALETTE (0x800) -#define SM501_DC_CRT_PALETTE (0xC00) +#define SM501_DC_CRT_PALETTE (0xC00) /* Zoom Video port base */ -#define SM501_ZVPORT (0x090000) +#define SM501_ZVPORT (0x090000) /* AC97/I2S base */ -#define SM501_AC97 (0x0A0000) +#define SM501_AC97 (0x0A0000) /* 8051 micro controller base */ -#define SM501_UCONTROLLER (0x0B0000) +#define SM501_UCONTROLLER (0x0B0000) /* 8051 micro controller SRAM base */ -#define SM501_UCONTROLLER_SRAM (0x0C0000) +#define SM501_UCONTROLLER_SRAM (0x0C0000) /* DMA base */ -#define SM501_DMA (0x0D0000) +#define SM501_DMA (0x0D0000) /* 2d engine base */ -#define SM501_2D_ENGINE (0x100000) -#define SM501_2D_SOURCE (0x00) -#define SM501_2D_DESTINATION (0x04) -#define SM501_2D_DIMENSION (0x08) -#define SM501_2D_CONTROL (0x0C) -#define SM501_2D_PITCH (0x10) -#define SM501_2D_FOREGROUND (0x14) -#define SM501_2D_BACKGROUND (0x18) -#define SM501_2D_STRETCH (0x1C) -#define SM501_2D_COLOR_COMPARE (0x20) -#define SM501_2D_COLOR_COMPARE_MASK (0x24) -#define SM501_2D_MASK (0x28) -#define SM501_2D_CLIP_TL (0x2C) -#define SM501_2D_CLIP_BR (0x30) -#define SM501_2D_MONO_PATTERN_LOW (0x34) -#define SM501_2D_MONO_PATTERN_HIGH (0x38) -#define SM501_2D_WINDOW_WIDTH (0x3C) -#define SM501_2D_SOURCE_BASE (0x40) -#define SM501_2D_DESTINATION_BASE (0x44) -#define SM501_2D_ALPHA (0x48) -#define SM501_2D_WRAP (0x4C) -#define SM501_2D_STATUS (0x50) - -#define SM501_CSC_Y_SOURCE_BASE (0xC8) -#define SM501_CSC_CONSTANTS (0xCC) -#define SM501_CSC_Y_SOURCE_X (0xD0) -#define SM501_CSC_Y_SOURCE_Y (0xD4) -#define SM501_CSC_U_SOURCE_BASE (0xD8) -#define SM501_CSC_V_SOURCE_BASE (0xDC) -#define SM501_CSC_SOURCE_DIMENSION (0xE0) -#define SM501_CSC_SOURCE_PITCH (0xE4) -#define SM501_CSC_DESTINATION (0xE8) -#define SM501_CSC_DESTINATION_DIMENSION (0xEC) -#define SM501_CSC_DESTINATION_PITCH (0xF0) -#define SM501_CSC_SCALE_FACTOR (0xF4) -#define SM501_CSC_DESTINATION_BASE (0xF8) -#define SM501_CSC_CONTROL (0xFC) +#define SM501_2D_ENGINE (0x100000) +#define SM501_2D_SOURCE (0x00) +#define SM501_2D_DESTINATION (0x04) +#define SM501_2D_DIMENSION (0x08) +#define SM501_2D_CONTROL (0x0C) +#define SM501_2D_PITCH (0x10) +#define SM501_2D_FOREGROUND (0x14) +#define SM501_2D_BACKGROUND (0x18) +#define SM501_2D_STRETCH (0x1C) +#define SM501_2D_COLOR_COMPARE (0x20) +#define SM501_2D_COLOR_COMPARE_MASK (0x24) +#define SM501_2D_MASK (0x28) +#define SM501_2D_CLIP_TL (0x2C) +#define SM501_2D_CLIP_BR (0x30) +#define SM501_2D_MONO_PATTERN_LOW (0x34) +#define SM501_2D_MONO_PATTERN_HIGH (0x38) +#define SM501_2D_WINDOW_WIDTH (0x3C) +#define SM501_2D_SOURCE_BASE (0x40) +#define SM501_2D_DESTINATION_BASE (0x44) +#define SM501_2D_ALPHA (0x48) +#define SM501_2D_WRAP (0x4C) +#define SM501_2D_STATUS (0x50) + +#define SM501_CSC_Y_SOURCE_BASE (0xC8) +#define SM501_CSC_CONSTANTS (0xCC) +#define SM501_CSC_Y_SOURCE_X (0xD0) +#define SM501_CSC_Y_SOURCE_Y (0xD4) +#define SM501_CSC_U_SOURCE_BASE (0xD8) +#define SM501_CSC_V_SOURCE_BASE (0xDC) +#define SM501_CSC_SOURCE_DIMENSION (0xE0) +#define SM501_CSC_SOURCE_PITCH (0xE4) +#define SM501_CSC_DESTINATION (0xE8) +#define SM501_CSC_DESTINATION_DIMENSION (0xEC) +#define SM501_CSC_DESTINATION_PITCH (0xF0) +#define SM501_CSC_SCALE_FACTOR (0xF4) +#define SM501_CSC_DESTINATION_BASE (0xF8) +#define SM501_CSC_CONTROL (0xFC) /* 2d engine data port base */ -#define SM501_2D_ENGINE_DATA (0x110000) +#define SM501_2D_ENGINE_DATA (0x110000) /* end of register definitions */ @@ -446,12 +453,12 @@ /* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ static const uint32_t sm501_mem_local_size[] = { - [0] = 4*1024*1024, - [1] = 8*1024*1024, - [2] = 16*1024*1024, - [3] = 32*1024*1024, - [4] = 64*1024*1024, - [5] = 2*1024*1024, + [0] = 4 * M_BYTE, + [1] = 8 * M_BYTE, + [2] = 16 * M_BYTE, + [3] = 32 * M_BYTE, + [4] = 64 * M_BYTE, + [5] = 2 * M_BYTE, }; #define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index] @@ -460,10 +467,13 @@ typedef struct SM501State { QemuConsole *con; /* status & internal resources */ - hwaddr base; uint32_t local_mem_size_index; - uint8_t * local_mem; + uint8_t *local_mem; MemoryRegion local_mem_region; + MemoryRegion mmio_region; + MemoryRegion system_config_region; + MemoryRegion disp_ctrl_region; + MemoryRegion twoD_engine_region; uint32_t last_width; uint32_t last_height; @@ -473,6 +483,7 @@ typedef struct SM501State { uint32_t gpio_31_0_control; uint32_t gpio_63_32_control; uint32_t dram_control; + uint32_t arbitration_control; uint32_t irq_mask; uint32_t misc_timing; uint32_t power_mode_control; @@ -482,7 +493,7 @@ typedef struct SM501State { uint32_t uart0_mcr; uint32_t uart0_scr; - uint8_t dc_palette[0x400 * 3]; + uint8_t dc_palette[DC_PALETTE_ENTRIES]; uint32_t dc_panel_control; uint32_t dc_panel_panning_control; @@ -502,6 +513,8 @@ typedef struct SM501State { uint32_t dc_panel_hwc_color_1_2; uint32_t dc_panel_hwc_color_3; + uint32_t dc_video_control; + uint32_t dc_crt_control; uint32_t dc_crt_fb_addr; uint32_t dc_crt_fb_offset; @@ -521,13 +534,20 @@ typedef struct SM501State { uint32_t twoD_control; uint32_t twoD_pitch; uint32_t twoD_foreground; + uint32_t twoD_background; uint32_t twoD_stretch; + uint32_t twoD_color_compare; uint32_t twoD_color_compare_mask; uint32_t twoD_mask; + uint32_t twoD_clip_tl; + uint32_t twoD_clip_br; + uint32_t twoD_mono_pattern_low; + uint32_t twoD_mono_pattern_high; uint32_t twoD_window_width; uint32_t twoD_source_base; uint32_t twoD_destination_base; - + uint32_t twoD_alpha; + uint32_t twoD_wrap; } SM501State; static uint32_t get_local_mem_size_index(uint32_t size) @@ -536,18 +556,36 @@ static uint32_t get_local_mem_size_index(uint32_t size) int i, index = 0; for (i = 0; i < ARRAY_SIZE(sm501_mem_local_size); i++) { - uint32_t new_size = sm501_mem_local_size[i]; - if (new_size >= size) { - if (norm_size == 0 || norm_size > new_size) { - norm_size = new_size; - index = i; - } - } + uint32_t new_size = sm501_mem_local_size[i]; + if (new_size >= size) { + if (norm_size == 0 || norm_size > new_size) { + norm_size = new_size; + index = i; + } + } } return index; } +static inline int get_width(SM501State *s, int crt) +{ + int width = crt ? s->dc_crt_h_total : s->dc_panel_h_total; + return (width & 0x00000FFF) + 1; +} + +static inline int get_height(SM501State *s, int crt) +{ + int height = crt ? s->dc_crt_v_total : s->dc_panel_v_total; + return (height & 0x00000FFF) + 1; +} + +static inline int get_bpp(SM501State *s, int crt) +{ + int bpp = crt ? s->dc_crt_control : s->dc_panel_control; + return 1 << (bpp & 3); +} + /** * Check the availability of hardware cursor. * @param crt 0 for PANEL, 1 for CRT. @@ -555,17 +593,17 @@ static uint32_t get_local_mem_size_index(uint32_t size) static inline int is_hwc_enabled(SM501State *state, int crt) { uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return addr & 0x80000000; + return addr & SM501_HWC_EN; } /** * Get the address which holds cursor pattern data. * @param crt 0 for PANEL, 1 for CRT. */ -static inline uint32_t get_hwc_address(SM501State *state, int crt) +static inline uint8_t *get_hwc_address(SM501State *state, int crt) { uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return (addr & 0x03FFFFF0)/* >> 4*/; + return state->local_mem + (addr & 0x03FFFFF0); } /** @@ -591,53 +629,51 @@ static inline uint32_t get_hwc_x(SM501State *state, int crt) } /** - * Get the cursor position in x coordinate. + * Get the hardware cursor palette. * @param crt 0 for PANEL, 1 for CRT. - * @param index 0, 1, 2 or 3 which specifies color of corsor dot. + * @param palette pointer to a [3 * 3] array to store color values in */ -static inline uint16_t get_hwc_color(SM501State *state, int crt, int index) +static inline void get_hwc_palette(SM501State *state, int crt, uint8_t *palette) { - uint32_t color_reg = 0; - uint16_t color_565 = 0; - - if (index == 0) { - return 0; - } - - switch (index) { - case 1: - case 2: - color_reg = crt ? state->dc_crt_hwc_color_1_2 - : state->dc_panel_hwc_color_1_2; - break; - case 3: - color_reg = crt ? state->dc_crt_hwc_color_3 - : state->dc_panel_hwc_color_3; - break; - default: - printf("invalid hw cursor color.\n"); - abort(); - } + int i; + uint32_t color_reg; + uint16_t rgb565; + + for (i = 0; i < 3; i++) { + if (i + 1 == 3) { + color_reg = crt ? state->dc_crt_hwc_color_3 + : state->dc_panel_hwc_color_3; + } else { + color_reg = crt ? state->dc_crt_hwc_color_1_2 + : state->dc_panel_hwc_color_1_2; + } - switch (index) { - case 1: - case 3: - color_565 = (uint16_t)(color_reg & 0xFFFF); - break; - case 2: - color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF); - break; + if (i + 1 == 2) { + rgb565 = (color_reg >> 16) & 0xFFFF; + } else { + rgb565 = color_reg & 0xFFFF; + } + palette[i * 3 + 0] = (rgb565 << 3) & 0xf8; /* red */ + palette[i * 3 + 1] = (rgb565 >> 3) & 0xfc; /* green */ + palette[i * 3 + 2] = (rgb565 >> 8) & 0xf8; /* blue */ } - return color_565; } -static int within_hwc_y_range(SM501State *state, int y, int crt) +static inline void hwc_invalidate(SM501State *s, int crt) { - int hwc_y = get_hwc_y(state, crt); - return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT); + int w = get_width(s, crt); + int h = get_height(s, crt); + int bpp = get_bpp(s, crt); + int start = get_hwc_y(s, crt); + int end = MIN(h, start + SM501_HWC_HEIGHT) + 1; + + start *= w * bpp; + end *= w * bpp; + + memory_region_set_dirty(&s->local_mem_region, start, end - start); } -static void sm501_2d_operation(SM501State * s) +static void sm501_2d_operation(SM501State *s) { /* obtain operation parameters */ int operation = (s->twoD_control >> 16) & 0x1f; @@ -653,8 +689,8 @@ static void sm501_2d_operation(SM501State * s) int addressing = (s->twoD_stretch >> 16) & 0xF; /* get frame buffer info */ - uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); - uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); + uint8_t *src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); + uint8_t *dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; @@ -671,20 +707,20 @@ static void sm501_2d_operation(SM501State * s) switch (operation) { case 0x00: /* copy area */ -#define COPY_AREA(_bpp, _pixel_type, rtl) { \ - int y, x, index_d, index_s; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ - if (rtl) { \ - index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ - index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ - } else { \ - index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ - index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - } \ - *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\ - } \ - } \ +#define COPY_AREA(_bpp, _pixel_type, rtl) { \ + int y, x, index_d, index_s; \ + for (y = 0; y < operation_height; y++) { \ + for (x = 0; x < operation_width; x++) { \ + if (rtl) { \ + index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ + index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ + } else { \ + index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ + index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + } \ + *(_pixel_type *)&dst[index_d] = *(_pixel_type *)&src[index_s];\ + } \ + } \ } switch (format_flags) { case 0: @@ -705,7 +741,7 @@ static void sm501_2d_operation(SM501State * s) for (y = 0; y < operation_height; y++) { \ for (x = 0; x < operation_width; x++) { \ int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - *(_pixel_type*)&dst[index] = (_pixel_type)color; \ + *(_pixel_type *)&dst[index] = (_pixel_type)color; \ } \ } \ } @@ -733,50 +769,53 @@ static void sm501_2d_operation(SM501State * s) static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; uint32_t ret = 0; SM501_DPRINTF("sm501 system config regs : read addr=%x\n", (int)addr); - switch(addr) { + switch (addr) { case SM501_SYSTEM_CONTROL: - ret = s->system_control; - break; + ret = s->system_control; + break; case SM501_MISC_CONTROL: - ret = s->misc_control; - break; + ret = s->misc_control; + break; case SM501_GPIO31_0_CONTROL: - ret = s->gpio_31_0_control; - break; + ret = s->gpio_31_0_control; + break; case SM501_GPIO63_32_CONTROL: - ret = s->gpio_63_32_control; - break; + ret = s->gpio_63_32_control; + break; case SM501_DEVICEID: - ret = 0x050100A0; - break; + ret = 0x050100A0; + break; case SM501_DRAM_CONTROL: - ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; - break; + ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; + break; + case SM501_ARBTRTN_CONTROL: + ret = s->arbitration_control; + break; case SM501_IRQ_MASK: - ret = s->irq_mask; - break; + ret = s->irq_mask; + break; case SM501_MISC_TIMING: - /* TODO : simulate gate control */ - ret = s->misc_timing; - break; + /* TODO : simulate gate control */ + ret = s->misc_timing; + break; case SM501_CURRENT_GATE: - /* TODO : simulate gate control */ - ret = 0x00021807; - break; + /* TODO : simulate gate control */ + ret = 0x00021807; + break; case SM501_CURRENT_CLOCK: - ret = 0x2A1A0A09; - break; + ret = 0x2A1A0A09; + break; case SM501_POWER_MODE_CONTROL: - ret = s->power_mode_control; - break; + ret = s->power_mode_control; + break; default: - printf("sm501 system config : not implemented register read." - " addr=%x\n", (int)addr); + printf("sm501 system config : not implemented register read." + " addr=%x\n", (int)addr); abort(); } @@ -786,47 +825,50 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, static void sm501_system_config_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n", - (uint32_t)addr, (uint32_t)value); + (uint32_t)addr, (uint32_t)value); - switch(addr) { + switch (addr) { case SM501_SYSTEM_CONTROL: - s->system_control = value & 0xE300B8F7; - break; + s->system_control = value & 0xE300B8F7; + break; case SM501_MISC_CONTROL: - s->misc_control = value & 0xFF7FFF20; - break; + s->misc_control = value & 0xFF7FFF20; + break; case SM501_GPIO31_0_CONTROL: - s->gpio_31_0_control = value; - break; + s->gpio_31_0_control = value; + break; case SM501_GPIO63_32_CONTROL: - s->gpio_63_32_control = value; - break; + s->gpio_63_32_control = value; + break; case SM501_DRAM_CONTROL: - s->local_mem_size_index = (value >> 13) & 0x7; - /* rODO : check validity of size change */ - s->dram_control |= value & 0x7FFFFFC3; - break; + s->local_mem_size_index = (value >> 13) & 0x7; + /* TODO : check validity of size change */ + s->dram_control |= value & 0x7FFFFFC3; + break; + case SM501_ARBTRTN_CONTROL: + s->arbitration_control = value & 0x37777777; + break; case SM501_IRQ_MASK: - s->irq_mask = value; - break; + s->irq_mask = value; + break; case SM501_MISC_TIMING: - s->misc_timing = value & 0xF31F1FFF; - break; + s->misc_timing = value & 0xF31F1FFF; + break; case SM501_POWER_MODE_0_GATE: case SM501_POWER_MODE_1_GATE: case SM501_POWER_MODE_0_CLOCK: case SM501_POWER_MODE_1_CLOCK: - /* TODO : simulate gate & clock control */ - break; + /* TODO : simulate gate & clock control */ + break; case SM501_POWER_MODE_CONTROL: - s->power_mode_control = value & 0x00000003; - break; + s->power_mode_control = value & 0x00000003; + break; default: - printf("sm501 system config : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (uint32_t)value); + printf("sm501 system config : not implemented register write." + " addr=%x, val=%x\n", (int)addr, (uint32_t)value); abort(); } } @@ -838,124 +880,128 @@ static const MemoryRegionOps sm501_system_config_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static uint32_t sm501_palette_read(void *opaque, hwaddr addr) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 palette read addr=%x\n", (int)addr); /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ assert(range_covers_byte(0, 0x400 * 3, addr)); - return *(uint32_t*)&s->dc_palette[addr]; + return *(uint32_t *)&s->dc_palette[addr]; } -static void sm501_palette_write(void *opaque, - hwaddr addr, uint32_t value) +static void sm501_palette_write(void *opaque, hwaddr addr, + uint32_t value) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 palette write addr=%x, val=%x\n", - (int)addr, value); + (int)addr, value); /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ assert(range_covers_byte(0, 0x400 * 3, addr)); - *(uint32_t*)&s->dc_palette[addr] = value; + *(uint32_t *)&s->dc_palette[addr] = value; } static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; uint32_t ret = 0; SM501_DPRINTF("sm501 disp ctrl regs : read addr=%x\n", (int)addr); - switch(addr) { + switch (addr) { case SM501_DC_PANEL_CONTROL: - ret = s->dc_panel_control; - break; + ret = s->dc_panel_control; + break; case SM501_DC_PANEL_PANNING_CONTROL: - ret = s->dc_panel_panning_control; - break; + ret = s->dc_panel_panning_control; + break; case SM501_DC_PANEL_FB_ADDR: - ret = s->dc_panel_fb_addr; - break; + ret = s->dc_panel_fb_addr; + break; case SM501_DC_PANEL_FB_OFFSET: - ret = s->dc_panel_fb_offset; - break; + ret = s->dc_panel_fb_offset; + break; case SM501_DC_PANEL_FB_WIDTH: - ret = s->dc_panel_fb_width; - break; + ret = s->dc_panel_fb_width; + break; case SM501_DC_PANEL_FB_HEIGHT: - ret = s->dc_panel_fb_height; - break; + ret = s->dc_panel_fb_height; + break; case SM501_DC_PANEL_TL_LOC: - ret = s->dc_panel_tl_location; - break; + ret = s->dc_panel_tl_location; + break; case SM501_DC_PANEL_BR_LOC: - ret = s->dc_panel_br_location; - break; + ret = s->dc_panel_br_location; + break; case SM501_DC_PANEL_H_TOT: - ret = s->dc_panel_h_total; - break; + ret = s->dc_panel_h_total; + break; case SM501_DC_PANEL_H_SYNC: - ret = s->dc_panel_h_sync; - break; + ret = s->dc_panel_h_sync; + break; case SM501_DC_PANEL_V_TOT: - ret = s->dc_panel_v_total; - break; + ret = s->dc_panel_v_total; + break; case SM501_DC_PANEL_V_SYNC: - ret = s->dc_panel_v_sync; - break; + ret = s->dc_panel_v_sync; + break; + + case SM501_DC_VIDEO_CONTROL: + ret = s->dc_video_control; + break; case SM501_DC_CRT_CONTROL: - ret = s->dc_crt_control; - break; + ret = s->dc_crt_control; + break; case SM501_DC_CRT_FB_ADDR: - ret = s->dc_crt_fb_addr; - break; + ret = s->dc_crt_fb_addr; + break; case SM501_DC_CRT_FB_OFFSET: - ret = s->dc_crt_fb_offset; - break; + ret = s->dc_crt_fb_offset; + break; case SM501_DC_CRT_H_TOT: - ret = s->dc_crt_h_total; - break; + ret = s->dc_crt_h_total; + break; case SM501_DC_CRT_H_SYNC: - ret = s->dc_crt_h_sync; - break; + ret = s->dc_crt_h_sync; + break; case SM501_DC_CRT_V_TOT: - ret = s->dc_crt_v_total; - break; + ret = s->dc_crt_v_total; + break; case SM501_DC_CRT_V_SYNC: - ret = s->dc_crt_v_sync; - break; + ret = s->dc_crt_v_sync; + break; case SM501_DC_CRT_HWC_ADDR: - ret = s->dc_crt_hwc_addr; - break; + ret = s->dc_crt_hwc_addr; + break; case SM501_DC_CRT_HWC_LOC: - ret = s->dc_crt_hwc_location; - break; + ret = s->dc_crt_hwc_location; + break; case SM501_DC_CRT_HWC_COLOR_1_2: - ret = s->dc_crt_hwc_color_1_2; - break; + ret = s->dc_crt_hwc_color_1_2; + break; case SM501_DC_CRT_HWC_COLOR_3: - ret = s->dc_crt_hwc_color_3; - break; + ret = s->dc_crt_hwc_color_3; + break; - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: + case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400 * 3 - 4: ret = sm501_palette_read(opaque, addr - SM501_DC_PANEL_PALETTE); break; default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); + printf("sm501 disp ctrl : not implemented register read." + " addr=%x\n", (int)addr); abort(); } @@ -965,104 +1011,124 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); + (unsigned)addr, (unsigned)value); - switch(addr) { + switch (addr) { case SM501_DC_PANEL_CONTROL: - s->dc_panel_control = value & 0x0FFF73FF; - break; + s->dc_panel_control = value & 0x0FFF73FF; + break; case SM501_DC_PANEL_PANNING_CONTROL: - s->dc_panel_panning_control = value & 0xFF3FFF3F; - break; + s->dc_panel_panning_control = value & 0xFF3FFF3F; + break; case SM501_DC_PANEL_FB_ADDR: - s->dc_panel_fb_addr = value & 0x8FFFFFF0; - break; + s->dc_panel_fb_addr = value & 0x8FFFFFF0; + break; case SM501_DC_PANEL_FB_OFFSET: - s->dc_panel_fb_offset = value & 0x3FF03FF0; - break; + s->dc_panel_fb_offset = value & 0x3FF03FF0; + break; case SM501_DC_PANEL_FB_WIDTH: - s->dc_panel_fb_width = value & 0x0FFF0FFF; - break; + s->dc_panel_fb_width = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_FB_HEIGHT: - s->dc_panel_fb_height = value & 0x0FFF0FFF; - break; + s->dc_panel_fb_height = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_TL_LOC: - s->dc_panel_tl_location = value & 0x07FF07FF; - break; + s->dc_panel_tl_location = value & 0x07FF07FF; + break; case SM501_DC_PANEL_BR_LOC: - s->dc_panel_br_location = value & 0x07FF07FF; - break; + s->dc_panel_br_location = value & 0x07FF07FF; + break; case SM501_DC_PANEL_H_TOT: - s->dc_panel_h_total = value & 0x0FFF0FFF; - break; + s->dc_panel_h_total = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_H_SYNC: - s->dc_panel_h_sync = value & 0x00FF0FFF; - break; + s->dc_panel_h_sync = value & 0x00FF0FFF; + break; case SM501_DC_PANEL_V_TOT: - s->dc_panel_v_total = value & 0x0FFF0FFF; - break; + s->dc_panel_v_total = value & 0x0FFF0FFF; + break; case SM501_DC_PANEL_V_SYNC: - s->dc_panel_v_sync = value & 0x003F0FFF; - break; + s->dc_panel_v_sync = value & 0x003F0FFF; + break; case SM501_DC_PANEL_HWC_ADDR: - s->dc_panel_hwc_addr = value & 0x8FFFFFF0; - break; + value &= 0x8FFFFFF0; + if (value != s->dc_panel_hwc_addr) { + hwc_invalidate(s, 0); + s->dc_panel_hwc_addr = value; + } + break; case SM501_DC_PANEL_HWC_LOC: - s->dc_panel_hwc_location = value & 0x0FFF0FFF; - break; + value &= 0x0FFF0FFF; + if (value != s->dc_panel_hwc_location) { + hwc_invalidate(s, 0); + s->dc_panel_hwc_location = value; + } + break; case SM501_DC_PANEL_HWC_COLOR_1_2: - s->dc_panel_hwc_color_1_2 = value; - break; + s->dc_panel_hwc_color_1_2 = value; + break; case SM501_DC_PANEL_HWC_COLOR_3: - s->dc_panel_hwc_color_3 = value & 0x0000FFFF; - break; + s->dc_panel_hwc_color_3 = value & 0x0000FFFF; + break; + + case SM501_DC_VIDEO_CONTROL: + s->dc_video_control = value & 0x00037FFF; + break; case SM501_DC_CRT_CONTROL: - s->dc_crt_control = value & 0x0003FFFF; - break; + s->dc_crt_control = value & 0x0003FFFF; + break; case SM501_DC_CRT_FB_ADDR: - s->dc_crt_fb_addr = value & 0x8FFFFFF0; - break; + s->dc_crt_fb_addr = value & 0x8FFFFFF0; + break; case SM501_DC_CRT_FB_OFFSET: - s->dc_crt_fb_offset = value & 0x3FF03FF0; - break; + s->dc_crt_fb_offset = value & 0x3FF03FF0; + break; case SM501_DC_CRT_H_TOT: - s->dc_crt_h_total = value & 0x0FFF0FFF; - break; + s->dc_crt_h_total = value & 0x0FFF0FFF; + break; case SM501_DC_CRT_H_SYNC: - s->dc_crt_h_sync = value & 0x00FF0FFF; - break; + s->dc_crt_h_sync = value & 0x00FF0FFF; + break; case SM501_DC_CRT_V_TOT: - s->dc_crt_v_total = value & 0x0FFF0FFF; - break; + s->dc_crt_v_total = value & 0x0FFF0FFF; + break; case SM501_DC_CRT_V_SYNC: - s->dc_crt_v_sync = value & 0x003F0FFF; - break; + s->dc_crt_v_sync = value & 0x003F0FFF; + break; case SM501_DC_CRT_HWC_ADDR: - s->dc_crt_hwc_addr = value & 0x8FFFFFF0; - break; + value &= 0x8FFFFFF0; + if (value != s->dc_crt_hwc_addr) { + hwc_invalidate(s, 1); + s->dc_crt_hwc_addr = value; + } + break; case SM501_DC_CRT_HWC_LOC: - s->dc_crt_hwc_location = value & 0x0FFF0FFF; - break; + value &= 0x0FFF0FFF; + if (value != s->dc_crt_hwc_location) { + hwc_invalidate(s, 1); + s->dc_crt_hwc_location = value; + } + break; case SM501_DC_CRT_HWC_COLOR_1_2: - s->dc_crt_hwc_color_1_2 = value; - break; + s->dc_crt_hwc_color_1_2 = value; + break; case SM501_DC_CRT_HWC_COLOR_3: - s->dc_crt_hwc_color_3 = value & 0x0000FFFF; - break; + s->dc_crt_hwc_color_3 = value & 0x0000FFFF; + break; - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: + case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400 * 3 - 4: sm501_palette_write(opaque, addr - SM501_DC_PANEL_PALETTE, value); break; default: - printf("sm501 disp ctrl : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); + printf("sm501 disp ctrl : not implemented register write." + " addr=%x, val=%x\n", (int)addr, (unsigned)value); abort(); } } @@ -1074,20 +1140,80 @@ static const MemoryRegionOps sm501_disp_ctrl_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; uint32_t ret = 0; SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr); - switch(addr) { + switch (addr) { + case SM501_2D_SOURCE: + ret = s->twoD_source; + break; + case SM501_2D_DESTINATION: + ret = s->twoD_destination; + break; + case SM501_2D_DIMENSION: + ret = s->twoD_dimension; + break; + case SM501_2D_CONTROL: + ret = s->twoD_control; + break; + case SM501_2D_PITCH: + ret = s->twoD_pitch; + break; + case SM501_2D_FOREGROUND: + ret = s->twoD_foreground; + break; + case SM501_2D_BACKGROUND: + ret = s->twoD_background; + break; + case SM501_2D_STRETCH: + ret = s->twoD_stretch; + break; + case SM501_2D_COLOR_COMPARE: + ret = s->twoD_color_compare; + break; + case SM501_2D_COLOR_COMPARE_MASK: + ret = s->twoD_color_compare_mask; + break; + case SM501_2D_MASK: + ret = s->twoD_mask; + break; + case SM501_2D_CLIP_TL: + ret = s->twoD_clip_tl; + break; + case SM501_2D_CLIP_BR: + ret = s->twoD_clip_br; + break; + case SM501_2D_MONO_PATTERN_LOW: + ret = s->twoD_mono_pattern_low; + break; + case SM501_2D_MONO_PATTERN_HIGH: + ret = s->twoD_mono_pattern_high; + break; + case SM501_2D_WINDOW_WIDTH: + ret = s->twoD_window_width; + break; case SM501_2D_SOURCE_BASE: ret = s->twoD_source_base; break; + case SM501_2D_DESTINATION_BASE: + ret = s->twoD_destination_base; + break; + case SM501_2D_ALPHA: + ret = s->twoD_alpha; + break; + case SM501_2D_WRAP: + ret = s->twoD_wrap; + break; + case SM501_2D_STATUS: + ret = 0; /* Should return interrupt status */ + break; default: printf("sm501 disp ctrl : not implemented register read." " addr=%x\n", (int)addr); @@ -1100,11 +1226,11 @@ static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, static void sm501_2d_engine_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State * s = (SM501State *)opaque; + SM501State *s = (SM501State *)opaque; SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", (unsigned)addr, (unsigned)value); - switch(addr) { + switch (addr) { case SM501_2D_SOURCE: s->twoD_source = value; break; @@ -1130,15 +1256,33 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, case SM501_2D_FOREGROUND: s->twoD_foreground = value; break; + case SM501_2D_BACKGROUND: + s->twoD_background = value; + break; case SM501_2D_STRETCH: s->twoD_stretch = value; break; + case SM501_2D_COLOR_COMPARE: + s->twoD_color_compare = value; + break; case SM501_2D_COLOR_COMPARE_MASK: s->twoD_color_compare_mask = value; break; case SM501_2D_MASK: s->twoD_mask = value; break; + case SM501_2D_CLIP_TL: + s->twoD_clip_tl = value; + break; + case SM501_2D_CLIP_BR: + s->twoD_clip_br = value; + break; + case SM501_2D_MONO_PATTERN_LOW: + s->twoD_mono_pattern_low = value; + break; + case SM501_2D_MONO_PATTERN_HIGH: + s->twoD_mono_pattern_high = value; + break; case SM501_2D_WINDOW_WIDTH: s->twoD_window_width = value; break; @@ -1148,6 +1292,15 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, case SM501_2D_DESTINATION_BASE: s->twoD_destination_base = value; break; + case SM501_2D_ALPHA: + s->twoD_alpha = value; + break; + case SM501_2D_WRAP: + s->twoD_wrap = value; + break; + case SM501_2D_STATUS: + /* ignored, writing 0 should clear interrupt status */ + break; default: printf("sm501 2d engine : not implemented register write." " addr=%x, val=%x\n", (int)addr, (unsigned)value); @@ -1162,16 +1315,17 @@ static const MemoryRegionOps sm501_2d_engine_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; /* draw line functions for all console modes */ typedef void draw_line_func(uint8_t *d, const uint8_t *s, - int width, const uint32_t *pal); + int width, const uint32_t *pal); -typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, - int c_y, uint8_t *d, int width); +typedef void draw_hwc_line_func(uint8_t *d, const uint8_t *s, + int width, const uint8_t *palette, + int c_x, int c_y); #define DEPTH 8 #include "sm501_template.h" @@ -1197,7 +1351,7 @@ typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, #define DEPTH 32 #include "sm501_template.h" -static draw_line_func * draw_line8_funcs[] = { +static draw_line_func *draw_line8_funcs[] = { draw_line8_8, draw_line8_15, draw_line8_16, @@ -1207,7 +1361,7 @@ static draw_line_func * draw_line8_funcs[] = { draw_line8_16bgr, }; -static draw_line_func * draw_line16_funcs[] = { +static draw_line_func *draw_line16_funcs[] = { draw_line16_8, draw_line16_15, draw_line16_16, @@ -1217,7 +1371,7 @@ static draw_line_func * draw_line16_funcs[] = { draw_line16_16bgr, }; -static draw_line_func * draw_line32_funcs[] = { +static draw_line_func *draw_line32_funcs[] = { draw_line32_8, draw_line32_15, draw_line32_16, @@ -1227,7 +1381,7 @@ static draw_line_func * draw_line32_funcs[] = { draw_line32_16bgr, }; -static draw_hwc_line_func * draw_hwc_line_funcs[] = { +static draw_hwc_line_func *draw_hwc_line_funcs[] = { draw_hwc_line_8, draw_hwc_line_15, draw_hwc_line_16, @@ -1242,7 +1396,7 @@ static inline int get_depth_index(DisplaySurface *surface) switch (surface_bits_per_pixel(surface)) { default: case 8: - return 0; + return 0; case 15: return 1; case 16: @@ -1256,203 +1410,459 @@ static inline int get_depth_index(DisplaySurface *surface) } } -static void sm501_draw_crt(SM501State * s) +static void sm501_update_display(void *opaque) { + SM501State *s = (SM501State *)opaque; DisplaySurface *surface = qemu_console_surface(s->con); - int y; - int width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int height = (s->dc_crt_v_total & 0x00000FFF) + 1; - - uint8_t * src = s->local_mem; - int src_bpp = 0; + int y, c_x = 0, c_y = 0; + int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; + int width = get_width(s, crt); + int height = get_height(s, crt); + int src_bpp = get_bpp(s, crt); int dst_bpp = surface_bytes_per_pixel(surface); - uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE - - SM501_DC_PANEL_PALETTE]; - uint8_t hwc_palette[3 * 3]; - int ds_depth_index = get_depth_index(surface); - draw_line_func * draw_line = NULL; - draw_hwc_line_func * draw_hwc_line = NULL; + int dst_depth_index = get_depth_index(surface); + draw_line_func *draw_line = NULL; + draw_hwc_line_func *draw_hwc_line = NULL; int full_update = 0; int y_start = -1; ram_addr_t page_min = ~0l; ram_addr_t page_max = 0l; - ram_addr_t offset = 0; + ram_addr_t offset; + uint32_t *palette; + uint8_t hwc_palette[3 * 3]; + uint8_t *hwc_src = NULL; + + if (!((crt ? s->dc_crt_control : s->dc_panel_control) + & SM501_DC_CRT_CONTROL_ENABLE)) { + return; + } + + palette = (uint32_t *)(crt ? &s->dc_palette[SM501_DC_CRT_PALETTE - + SM501_DC_PANEL_PALETTE] + : &s->dc_palette[0]); /* choose draw_line function */ - switch (s->dc_crt_control & 3) { - case SM501_DC_CRT_CONTROL_8BPP: - src_bpp = 1; - draw_line = draw_line8_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_16BPP: - src_bpp = 2; - draw_line = draw_line16_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_32BPP: - src_bpp = 4; - draw_line = draw_line32_funcs[ds_depth_index]; - break; + switch (src_bpp) { + case 1: + draw_line = draw_line8_funcs[dst_depth_index]; + break; + case 2: + draw_line = draw_line16_funcs[dst_depth_index]; + break; + case 4: + draw_line = draw_line32_funcs[dst_depth_index]; + break; default: - printf("sm501 draw crt : invalid DC_CRT_CONTROL=%x.\n", - s->dc_crt_control); + printf("sm501 update display : invalid control register value.\n"); abort(); - break; + break; } /* set up to draw hardware cursor */ - if (is_hwc_enabled(s, 1)) { - int i; - - /* get cursor palette */ - for (i = 0; i < 3; i++) { - uint16_t rgb565 = get_hwc_color(s, 1, i + 1); - hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */ - hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */ - hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */ - } - + if (is_hwc_enabled(s, crt)) { /* choose cursor draw line function */ - draw_hwc_line = draw_hwc_line_funcs[ds_depth_index]; + draw_hwc_line = draw_hwc_line_funcs[dst_depth_index]; + hwc_src = get_hwc_address(s, crt); + c_x = get_hwc_x(s, crt); + c_y = get_hwc_y(s, crt); + get_hwc_palette(s, crt, hwc_palette); } /* adjust console size */ if (s->last_width != width || s->last_height != height) { qemu_console_resize(s->con, width, height); surface = qemu_console_surface(s->con); - s->last_width = width; - s->last_height = height; - full_update = 1; + s->last_width = width; + s->last_height = height; + full_update = 1; } /* draw each line according to conditions */ memory_region_sync_dirty_bitmap(&s->local_mem_region); - for (y = 0; y < height; y++) { - int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0; - int update = full_update || update_hwc; + for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) { + int update, update_hwc; ram_addr_t page0 = offset; ram_addr_t page1 = offset + width * src_bpp - 1; - /* check dirty flags for each line */ - update = memory_region_get_dirty(&s->local_mem_region, page0, - page1 - page0, DIRTY_MEMORY_VGA); + /* check if hardware cursor is enabled and we're within its range */ + update_hwc = draw_hwc_line && c_y <= y && y < c_y + SM501_HWC_HEIGHT; + update = full_update || update_hwc; + /* check dirty flags for each line */ + update |= memory_region_get_dirty(&s->local_mem_region, page0, + page1 - page0, DIRTY_MEMORY_VGA); - /* draw line and change status */ - if (update) { + /* draw line and change status */ + if (update) { uint8_t *d = surface_data(surface); d += y * width * dst_bpp; /* draw graphics layer */ - draw_line(d, src, width, palette); + draw_line(d, s->local_mem + offset, width, palette); - /* draw haredware cursor */ + /* draw hardware cursor */ if (update_hwc) { - draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width); + draw_hwc_line(d, hwc_src, width, hwc_palette, c_x, y - c_y); } - if (y_start < 0) - y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; - } else { - if (y_start >= 0) { - /* flush to display */ + if (y_start < 0) { + y_start = y; + } + if (page0 < page_min) { + page_min = page0; + } + if (page1 > page_max) { + page_max = page1; + } + } else { + if (y_start >= 0) { + /* flush to display */ dpy_gfx_update(s->con, 0, y_start, width, y - y_start); - y_start = -1; - } - } - - src += width * src_bpp; - offset += width * src_bpp; + y_start = -1; + } + } } /* complete flush to display */ - if (y_start >= 0) + if (y_start >= 0) { dpy_gfx_update(s->con, 0, y_start, width, y - y_start); + } /* clear dirty flags */ if (page_min != ~0l) { - memory_region_reset_dirty(&s->local_mem_region, + memory_region_reset_dirty(&s->local_mem_region, page_min, page_max + TARGET_PAGE_SIZE, DIRTY_MEMORY_VGA); } } -static void sm501_update_display(void *opaque) -{ - SM501State * s = (SM501State *)opaque; - - if (s->dc_crt_control & SM501_DC_CRT_CONTROL_ENABLE) - sm501_draw_crt(s); -} - static const GraphicHwOps sm501_ops = { .gfx_update = sm501_update_display, }; -void sm501_init(MemoryRegion *address_space_mem, uint32_t base, - uint32_t local_mem_bytes, qemu_irq irq, Chardev *chr) +static void sm501_reset(SM501State *s) { - SM501State * s; - DeviceState *dev; - MemoryRegion *sm501_system_config = g_new(MemoryRegion, 1); - MemoryRegion *sm501_disp_ctrl = g_new(MemoryRegion, 1); - MemoryRegion *sm501_2d_engine = g_new(MemoryRegion, 1); - - /* allocate management data region */ - s = (SM501State *)g_malloc0(sizeof(SM501State)); - s->base = base; - s->local_mem_size_index - = get_local_mem_size_index(local_mem_bytes); - SM501_DPRINTF("local mem size=%x. index=%d\n", get_local_mem_size(s), - s->local_mem_size_index); - s->system_control = 0x00100000; - s->misc_control = 0x00001000; /* assumes SH, active=low */ - s->dc_panel_control = 0x00010000; + s->system_control = 0x00100000; /* 2D engine FIFO empty */ + /* Bits 17 (SH), 7 (CDR), 6:5 (Test), 2:0 (Bus) are all supposed + * to be determined at reset by GPIO lines which set config bits. + * We hardwire them: + * SH = 0 : Hitachi Ready Polarity == Active Low + * CDR = 0 : do not reset clock divider + * TEST = 0 : Normal mode (not testing the silicon) + * BUS = 0 : Hitachi SH3/SH4 + */ + s->misc_control = SM501_MISC_DAC_POWER; + s->gpio_31_0_control = 0; + s->gpio_63_32_control = 0; + s->dram_control = 0; + s->arbitration_control = 0x05146732; + s->irq_mask = 0; + s->misc_timing = 0; + s->power_mode_control = 0; + s->dc_panel_control = 0x00010000; /* FIFO level 3 */ + s->dc_video_control = 0; s->dc_crt_control = 0x00010000; + s->twoD_source = 0; + s->twoD_destination = 0; + s->twoD_dimension = 0; + s->twoD_control = 0; + s->twoD_pitch = 0; + s->twoD_foreground = 0; + s->twoD_background = 0; + s->twoD_stretch = 0; + s->twoD_color_compare = 0; + s->twoD_color_compare_mask = 0; + s->twoD_mask = 0; + s->twoD_clip_tl = 0; + s->twoD_clip_br = 0; + s->twoD_mono_pattern_low = 0; + s->twoD_mono_pattern_high = 0; + s->twoD_window_width = 0; + s->twoD_source_base = 0; + s->twoD_destination_base = 0; + s->twoD_alpha = 0; + s->twoD_wrap = 0; +} + +static void sm501_init(SM501State *s, DeviceState *dev, + uint32_t local_mem_bytes) +{ + s->local_mem_size_index = get_local_mem_size_index(local_mem_bytes); + SM501_DPRINTF("sm501 local mem size=%x. index=%d\n", get_local_mem_size(s), + s->local_mem_size_index); - /* allocate local memory */ - memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local", - local_mem_bytes, &error_fatal); + /* local memory */ + memory_region_init_ram(&s->local_mem_region, OBJECT(dev), "sm501.local", + get_local_mem_size(s), &error_fatal); vmstate_register_ram_global(&s->local_mem_region); memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA); s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); - memory_region_add_subregion(address_space_mem, base, &s->local_mem_region); - /* map mmio */ - memory_region_init_io(sm501_system_config, NULL, &sm501_system_config_ops, s, + /* mmio */ + memory_region_init(&s->mmio_region, OBJECT(dev), "sm501.mmio", MMIO_SIZE); + memory_region_init_io(&s->system_config_region, OBJECT(dev), + &sm501_system_config_ops, s, "sm501-system-config", 0x6c); - memory_region_add_subregion(address_space_mem, base + MMIO_BASE_OFFSET, - sm501_system_config); - memory_region_init_io(sm501_disp_ctrl, NULL, &sm501_disp_ctrl_ops, s, + memory_region_add_subregion(&s->mmio_region, SM501_SYS_CONFIG, + &s->system_config_region); + memory_region_init_io(&s->disp_ctrl_region, OBJECT(dev), + &sm501_disp_ctrl_ops, s, "sm501-disp-ctrl", 0x1000); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_DC, - sm501_disp_ctrl); - memory_region_init_io(sm501_2d_engine, NULL, &sm501_2d_engine_ops, s, + memory_region_add_subregion(&s->mmio_region, SM501_DC, + &s->disp_ctrl_region); + memory_region_init_io(&s->twoD_engine_region, OBJECT(dev), + &sm501_2d_engine_ops, s, "sm501-2d-engine", 0x54); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, - sm501_2d_engine); + memory_region_add_subregion(&s->mmio_region, SM501_2D_ENGINE, + &s->twoD_engine_region); + + /* create qemu graphic console */ + s->con = graphic_console_init(DEVICE(dev), 0, &sm501_ops, s); +} + +static const VMStateDescription vmstate_sm501_state = { + .name = "sm501-state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(local_mem_size_index, SM501State), + VMSTATE_UINT32(system_control, SM501State), + VMSTATE_UINT32(misc_control, SM501State), + VMSTATE_UINT32(gpio_31_0_control, SM501State), + VMSTATE_UINT32(gpio_63_32_control, SM501State), + VMSTATE_UINT32(dram_control, SM501State), + VMSTATE_UINT32(arbitration_control, SM501State), + VMSTATE_UINT32(irq_mask, SM501State), + VMSTATE_UINT32(misc_timing, SM501State), + VMSTATE_UINT32(power_mode_control, SM501State), + VMSTATE_UINT32(uart0_ier, SM501State), + VMSTATE_UINT32(uart0_lcr, SM501State), + VMSTATE_UINT32(uart0_mcr, SM501State), + VMSTATE_UINT32(uart0_scr, SM501State), + VMSTATE_UINT8_ARRAY(dc_palette, SM501State, DC_PALETTE_ENTRIES), + VMSTATE_UINT32(dc_panel_control, SM501State), + VMSTATE_UINT32(dc_panel_panning_control, SM501State), + VMSTATE_UINT32(dc_panel_fb_addr, SM501State), + VMSTATE_UINT32(dc_panel_fb_offset, SM501State), + VMSTATE_UINT32(dc_panel_fb_width, SM501State), + VMSTATE_UINT32(dc_panel_fb_height, SM501State), + VMSTATE_UINT32(dc_panel_tl_location, SM501State), + VMSTATE_UINT32(dc_panel_br_location, SM501State), + VMSTATE_UINT32(dc_panel_h_total, SM501State), + VMSTATE_UINT32(dc_panel_h_sync, SM501State), + VMSTATE_UINT32(dc_panel_v_total, SM501State), + VMSTATE_UINT32(dc_panel_v_sync, SM501State), + VMSTATE_UINT32(dc_panel_hwc_addr, SM501State), + VMSTATE_UINT32(dc_panel_hwc_location, SM501State), + VMSTATE_UINT32(dc_panel_hwc_color_1_2, SM501State), + VMSTATE_UINT32(dc_panel_hwc_color_3, SM501State), + VMSTATE_UINT32(dc_video_control, SM501State), + VMSTATE_UINT32(dc_crt_control, SM501State), + VMSTATE_UINT32(dc_crt_fb_addr, SM501State), + VMSTATE_UINT32(dc_crt_fb_offset, SM501State), + VMSTATE_UINT32(dc_crt_h_total, SM501State), + VMSTATE_UINT32(dc_crt_h_sync, SM501State), + VMSTATE_UINT32(dc_crt_v_total, SM501State), + VMSTATE_UINT32(dc_crt_v_sync, SM501State), + VMSTATE_UINT32(dc_crt_hwc_addr, SM501State), + VMSTATE_UINT32(dc_crt_hwc_location, SM501State), + VMSTATE_UINT32(dc_crt_hwc_color_1_2, SM501State), + VMSTATE_UINT32(dc_crt_hwc_color_3, SM501State), + VMSTATE_UINT32(twoD_source, SM501State), + VMSTATE_UINT32(twoD_destination, SM501State), + VMSTATE_UINT32(twoD_dimension, SM501State), + VMSTATE_UINT32(twoD_control, SM501State), + VMSTATE_UINT32(twoD_pitch, SM501State), + VMSTATE_UINT32(twoD_foreground, SM501State), + VMSTATE_UINT32(twoD_background, SM501State), + VMSTATE_UINT32(twoD_stretch, SM501State), + VMSTATE_UINT32(twoD_color_compare, SM501State), + VMSTATE_UINT32(twoD_color_compare_mask, SM501State), + VMSTATE_UINT32(twoD_mask, SM501State), + VMSTATE_UINT32(twoD_clip_tl, SM501State), + VMSTATE_UINT32(twoD_clip_br, SM501State), + VMSTATE_UINT32(twoD_mono_pattern_low, SM501State), + VMSTATE_UINT32(twoD_mono_pattern_high, SM501State), + VMSTATE_UINT32(twoD_window_width, SM501State), + VMSTATE_UINT32(twoD_source_base, SM501State), + VMSTATE_UINT32(twoD_destination_base, SM501State), + VMSTATE_UINT32(twoD_alpha, SM501State), + VMSTATE_UINT32(twoD_wrap, SM501State), + VMSTATE_END_OF_LIST() + } +}; + +#define TYPE_SYSBUS_SM501 "sysbus-sm501" +#define SYSBUS_SM501(obj) \ + OBJECT_CHECK(SM501SysBusState, (obj), TYPE_SYSBUS_SM501) + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + SM501State state; + uint32_t vram_size; + uint32_t base; + void *chr_state; +} SM501SysBusState; + +static void sm501_realize_sysbus(DeviceState *dev, Error **errp) +{ + SM501SysBusState *s = SYSBUS_SM501(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + DeviceState *usb_dev; + + sm501_init(&s->state, dev, s->vram_size); + if (get_local_mem_size(&s->state) != s->vram_size) { + error_setg(errp, "Invalid VRAM size, nearest valid size is %" PRIu32, + get_local_mem_size(&s->state)); + return; + } + sysbus_init_mmio(sbd, &s->state.local_mem_region); + sysbus_init_mmio(sbd, &s->state.mmio_region); /* bridge to usb host emulation module */ - dev = qdev_create(NULL, "sysbus-ohci"); - qdev_prop_set_uint32(dev, "num-ports", 2); - qdev_prop_set_uint64(dev, "dma-offset", base); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, - base + MMIO_BASE_OFFSET + SM501_USB_HOST); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + usb_dev = qdev_create(NULL, "sysbus-ohci"); + qdev_prop_set_uint32(usb_dev, "num-ports", 2); + qdev_prop_set_uint64(usb_dev, "dma-offset", s->base); + qdev_init_nofail(usb_dev); + memory_region_add_subregion(&s->state.mmio_region, SM501_USB_HOST, + sysbus_mmio_get_region(SYS_BUS_DEVICE(usb_dev), 0)); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(usb_dev)); /* bridge to serial emulation module */ - if (chr) { - serial_mm_init(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_UART0, 2, + if (s->chr_state) { + serial_mm_init(&s->state.mmio_region, SM501_UART0, 2, NULL, /* TODO : chain irq to IRL */ - 115200, chr, DEVICE_NATIVE_ENDIAN); + 115200, s->chr_state, DEVICE_LITTLE_ENDIAN); } +} - /* create qemu graphic console */ - s->con = graphic_console_init(DEVICE(dev), 0, &sm501_ops, s); +static Property sm501_sysbus_properties[] = { + DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), + DEFINE_PROP_UINT32("base", SM501SysBusState, base, 0), + DEFINE_PROP_PTR("chr-state", SM501SysBusState, chr_state), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sm501_reset_sysbus(DeviceState *dev) +{ + SM501SysBusState *s = SYSBUS_SM501(dev); + sm501_reset(&s->state); } + +static const VMStateDescription vmstate_sm501_sysbus = { + .name = TYPE_SYSBUS_SM501, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(state, SM501SysBusState, 1, + vmstate_sm501_state, SM501State), + VMSTATE_END_OF_LIST() + } +}; + +static void sm501_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = sm501_realize_sysbus; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->desc = "SM501 Multimedia Companion"; + dc->props = sm501_sysbus_properties; + dc->reset = sm501_reset_sysbus; + dc->vmsd = &vmstate_sm501_sysbus; + /* Note: pointer property "chr-state" may remain null, thus + * no need for dc->cannot_instantiate_with_device_add_yet = true; + */ +} + +static const TypeInfo sm501_sysbus_info = { + .name = TYPE_SYSBUS_SM501, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SM501SysBusState), + .class_init = sm501_sysbus_class_init, +}; + +#define TYPE_PCI_SM501 "sm501" +#define PCI_SM501(obj) OBJECT_CHECK(SM501PCIState, (obj), TYPE_PCI_SM501) + +typedef struct { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + SM501State state; + uint32_t vram_size; +} SM501PCIState; + +static void sm501_realize_pci(PCIDevice *dev, Error **errp) +{ + SM501PCIState *s = PCI_SM501(dev); + + sm501_init(&s->state, DEVICE(dev), s->vram_size); + if (get_local_mem_size(&s->state) != s->vram_size) { + error_setg(errp, "Invalid VRAM size, nearest valid size is %" PRIu32, + get_local_mem_size(&s->state)); + return; + } + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->state.local_mem_region); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->state.mmio_region); +} + +static Property sm501_pci_properties[] = { + DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * M_BYTE), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sm501_reset_pci(DeviceState *dev) +{ + SM501PCIState *s = PCI_SM501(dev); + sm501_reset(&s->state); + /* Bits 2:0 of misc_control register is 001 for PCI */ + s->state.misc_control |= 1; +} + +static const VMStateDescription vmstate_sm501_pci = { + .name = TYPE_PCI_SM501, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, SM501PCIState), + VMSTATE_STRUCT(state, SM501PCIState, 1, + vmstate_sm501_state, SM501State), + VMSTATE_END_OF_LIST() + } +}; + +static void sm501_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = sm501_realize_pci; + k->vendor_id = PCI_VENDOR_ID_SILICON_MOTION; + k->device_id = PCI_DEVICE_ID_SM501; + k->class_id = PCI_CLASS_DISPLAY_OTHER; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->desc = "SM501 Display Controller"; + dc->props = sm501_pci_properties; + dc->reset = sm501_reset_pci; + dc->hotpluggable = false; + dc->vmsd = &vmstate_sm501_pci; +} + +static const TypeInfo sm501_pci_info = { + .name = TYPE_PCI_SM501, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(SM501PCIState), + .class_init = sm501_pci_class_init, +}; + +static void sm501_register_types(void) +{ + type_register_static(&sm501_sysbus_info); + type_register_static(&sm501_pci_info); +} + +type_init(sm501_register_types) diff --git a/hw/display/sm501_template.h b/hw/display/sm501_template.h index f33e499be4..a60abad019 100644 --- a/hw/display/sm501_template.h +++ b/hw/display/sm501_template.h @@ -47,81 +47,67 @@ static void glue(draw_line8_, PIXEL_NAME)( { uint8_t v, r, g, b; do { - v = ldub_p(s); - r = (pal[v] >> 16) & 0xff; - g = (pal[v] >> 8) & 0xff; - b = (pal[v] >> 0) & 0xff; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s ++; - d += BPP; - } while (-- width != 0); + v = ldub_p(s); + r = (pal[v] >> 16) & 0xff; + g = (pal[v] >> 8) & 0xff; + b = (pal[v] >> 0) & 0xff; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s++; + d += BPP; + } while (--width != 0); } static void glue(draw_line16_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) + uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) { uint16_t rgb565; uint8_t r, g, b; do { - rgb565 = lduw_p(s); - r = ((rgb565 >> 11) & 0x1f) << 3; - g = ((rgb565 >> 5) & 0x3f) << 2; - b = ((rgb565 >> 0) & 0x1f) << 3; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s += 2; - d += BPP; - } while (-- width != 0); + rgb565 = lduw_le_p(s); + r = (rgb565 >> 8) & 0xf8; + g = (rgb565 >> 3) & 0xfc; + b = (rgb565 << 3) & 0xf8; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 2; + d += BPP; + } while (--width != 0); } static void glue(draw_line32_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) + uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) { uint8_t r, g, b; do { - ldub_p(s); -#if defined(TARGET_WORDS_BIGENDIAN) - r = s[1]; - g = s[2]; - b = s[3]; -#else - b = s[0]; - g = s[1]; r = s[2]; -#endif - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s += 4; - d += BPP; - } while (-- width != 0); + g = s[1]; + b = s[0]; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 4; + d += BPP; + } while (--width != 0); } /** * Draw hardware cursor image on the given line. */ -static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt, - uint8_t * palette, int c_y, uint8_t *d, int width) +static void glue(draw_hwc_line_, PIXEL_NAME)(uint8_t *d, const uint8_t *s, + int width, const uint8_t *palette, int c_x, int c_y) { - int x, i; - uint8_t bitset = 0; - - /* get hardware cursor pattern */ - uint32_t cursor_addr = get_hwc_address(s, crt); - assert(0 <= c_y && c_y < SM501_HWC_HEIGHT); - cursor_addr += 64 * c_y / 4; /* 4 pixels per byte */ - cursor_addr += s->base; + int i; + uint8_t r, g, b, v, bitset = 0; /* get cursor position */ - x = get_hwc_x(s, crt); - d += x * BPP; - - for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) { - uint8_t v; + assert(0 <= c_y && c_y < SM501_HWC_HEIGHT); + s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */ + d += c_x * BPP; + for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) { /* get pixel value */ if (i % 4 == 0) { - bitset = ldub_phys(&address_space_memory, cursor_addr); - cursor_addr++; + bitset = ldub_p(s); + s++; } v = bitset & 3; bitset >>= 2; @@ -129,10 +115,10 @@ static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt, /* write pixel */ if (v) { v--; - uint8_t r = palette[v * 3 + 0]; - uint8_t g = palette[v * 3 + 1]; - uint8_t b = palette[v * 3 + 2]; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + r = palette[v * 3 + 0]; + g = palette[v * 3 + 1]; + b = palette[v * 3 + 2]; + *(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); } d += BPP; } diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 8e26aae801..5a1115cc65 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" -#include "cpu.h" /* FIXME shouldn't use TARGET_PAGE_SIZE */ #include "ui/console.h" #include "ui/pixel_ops.h" #include "hw/loader.h" @@ -93,41 +92,46 @@ typedef struct TCXState { uint16_t cursy; } TCXState; -static void tcx_set_dirty(TCXState *s) +static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len) { - memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); + memory_region_set_dirty(&s->vram_mem, addr, len); + + if (s->depth == 24) { + memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4, + len * 4); + memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4, + len * 4); + } } -static inline int tcx24_check_dirty(TCXState *s, ram_addr_t page, - ram_addr_t page24, ram_addr_t cpage) +static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len) { int ret; - ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); + ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); + + if (s->depth == 24) { + ret |= memory_region_get_dirty(&s->vram_mem, + s->vram24_offset + addr * 4, len * 4, + DIRTY_MEMORY_VGA); + ret |= memory_region_get_dirty(&s->vram_mem, + s->cplane_offset + addr * 4, len * 4, + DIRTY_MEMORY_VGA); + } + return ret; } -static inline void tcx24_reset_dirty(TCXState *ts, ram_addr_t page_min, - ram_addr_t page_max, ram_addr_t page24, - ram_addr_t cpage) +static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len) { - memory_region_reset_dirty(&ts->vram_mem, - page_min, - (page_max - page_min) + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - page24 + page_min * 4, - (page_max - page_min) * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - cpage + page_min * 4, - (page_max - page_min) * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); + + if (s->depth == 24) { + memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4, + len * 4, DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4, + len * 4, DIRTY_MEMORY_VGA); + } } static void update_palette_entries(TCXState *s, int start, int end) @@ -136,27 +140,14 @@ static void update_palette_entries(TCXState *s, int start, int end) int i; for (i = start; i < end; i++) { - switch (surface_bits_per_pixel(surface)) { - default: - case 8: - s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); - break; - case 15: - s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); - break; - case 16: - s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); - break; - case 32: - if (is_surface_bgr(surface)) { - s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); - } else { - s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); - } - break; + if (is_surface_bgr(surface)) { + s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); + } else { + s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); } + break; } - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); } static void tcx_draw_line32(TCXState *s1, uint8_t *d, @@ -172,31 +163,6 @@ static void tcx_draw_line32(TCXState *s1, uint8_t *d, } } -static void tcx_draw_line16(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - uint16_t *p = (uint16_t *)d; - - for (x = 0; x < width; x++) { - val = *s++; - *p++ = s1->palette[val]; - } -} - -static void tcx_draw_line8(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - - for(x = 0; x < width; x++) { - val = *s++; - *d++ = s1->palette[val]; - } -} - static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, int y, int width) { @@ -223,57 +189,6 @@ static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, } } -static void tcx_draw_cursor16(TCXState *s1, uint8_t *d, - int y, int width) -{ - int x, len; - uint32_t mask, bits; - uint16_t *p = (uint16_t *)d; - - y = y - s1->cursy; - mask = s1->cursmask[y]; - bits = s1->cursbits[y]; - len = MIN(width - s1->cursx, 32); - p = &p[s1->cursx]; - for (x = 0; x < len; x++) { - if (mask & 0x80000000) { - if (bits & 0x80000000) { - *p = s1->palette[259]; - } else { - *p = s1->palette[258]; - } - } - p++; - mask <<= 1; - bits <<= 1; - } -} - -static void tcx_draw_cursor8(TCXState *s1, uint8_t *d, - int y, int width) -{ - int x, len; - uint32_t mask, bits; - - y = y - s1->cursy; - mask = s1->cursmask[y]; - bits = s1->cursbits[y]; - len = MIN(width - s1->cursx, 32); - d = &d[s1->cursx]; - for (x = 0; x < len; x++) { - if (mask & 0x80000000) { - if (bits & 0x80000000) { - *d = s1->palette[259]; - } else { - *d = s1->palette[258]; - } - } - d++; - mask <<= 1; - bits <<= 1; - } -} - /* XXX Could be much more optimal: * detect if line/page/whole screen is in 24 bit mode @@ -322,10 +237,8 @@ static void tcx_update_display(void *opaque) ram_addr_t page, page_min, page_max; int y, y_start, dd, ds; uint8_t *d, *s; - void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); - void (*fc)(TCXState *s1, uint8_t *dst, int y, int width); - if (surface_bits_per_pixel(surface) == 0) { + if (surface_bits_per_pixel(surface) != 32) { return; } @@ -338,29 +251,9 @@ static void tcx_update_display(void *opaque) dd = surface_stride(surface); ds = 1024; - switch (surface_bits_per_pixel(surface)) { - case 32: - f = tcx_draw_line32; - fc = tcx_draw_cursor32; - break; - case 15: - case 16: - f = tcx_draw_line16; - fc = tcx_draw_cursor16; - break; - default: - case 8: - f = tcx_draw_line8; - fc = tcx_draw_cursor8; - break; - case 0: - return; - } - memory_region_sync_dirty_bitmap(&ts->vram_mem); - for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) { - if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA)) { + for (y = 0; y < ts->height; y++, page += ds) { + if (tcx_check_dirty(ts, page, ds)) { if (y_start < 0) y_start = y; if (page < page_min) @@ -368,37 +261,10 @@ static void tcx_update_display(void *opaque) if (page > page_max) page_max = page; - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); + tcx_draw_line32(ts, d, s, ts->width); if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); + tcx_draw_cursor32(ts, d, y, ts->width); } - d += dd; - s += ds; - y++; } else { if (y_start >= 0) { /* flush to display */ @@ -406,10 +272,9 @@ static void tcx_update_display(void *opaque) ts->width, y - y_start); y_start = -1; } - d += dd * 4; - s += ds * 4; - y += 4; } + s += ds; + d += dd; } if (y_start >= 0) { /* flush to display */ @@ -418,10 +283,7 @@ static void tcx_update_display(void *opaque) } /* reset modified pages */ if (page_max >= page_min) { - memory_region_reset_dirty(&ts->vram_mem, - page_min, - (page_max - page_min) + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); + tcx_reset_dirty(ts, page_min, page_max - page_min); } } @@ -429,7 +291,7 @@ static void tcx24_update_display(void *opaque) { TCXState *ts = opaque; DisplaySurface *surface = qemu_console_surface(ts->con); - ram_addr_t page, page_min, page_max, cpage, page24; + ram_addr_t page, page_min, page_max; int y, y_start, dd, ds; uint8_t *d, *s; uint32_t *cptr, *s24; @@ -439,8 +301,6 @@ static void tcx24_update_display(void *opaque) } page = 0; - page24 = ts->vram24_offset; - cpage = ts->cplane_offset; y_start = -1; page_min = -1; page_max = 0; @@ -452,9 +312,8 @@ static void tcx24_update_display(void *opaque) ds = 1024; memory_region_sync_dirty_bitmap(&ts->vram_mem); - for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE, - page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { - if (tcx24_check_dirty(ts, page, page24, cpage)) { + for (y = 0; y < ts->height; y++, page += ds) { + if (tcx_check_dirty(ts, page, ds)) { if (y_start < 0) y_start = y; if (page < page_min) @@ -465,38 +324,6 @@ static void tcx24_update_display(void *opaque) if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { tcx_draw_cursor32(ts, d, y, ts->width); } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; } else { if (y_start >= 0) { /* flush to display */ @@ -504,12 +331,11 @@ static void tcx24_update_display(void *opaque) ts->width, y - y_start); y_start = -1; } - d += dd * 4; - s += ds * 4; - cptr += ds * 4; - s24 += ds * 4; - y += 4; } + d += dd; + s += ds; + cptr += ds; + s24 += ds; } if (y_start >= 0) { /* flush to display */ @@ -518,7 +344,7 @@ static void tcx24_update_display(void *opaque) } /* reset modified pages */ if (page_max >= page_min) { - tcx24_reset_dirty(ts, page_min, page_max, page24, cpage); + tcx_reset_dirty(ts, page_min, page_max - page_min); } } @@ -526,7 +352,7 @@ static void tcx_invalidate_display(void *opaque) { TCXState *s = opaque; - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); qemu_console_resize(s->con, s->width, s->height); } @@ -534,7 +360,7 @@ static void tcx24_invalidate_display(void *opaque) { TCXState *s = opaque; - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); qemu_console_resize(s->con, s->width, s->height); } @@ -543,7 +369,7 @@ static int vmstate_tcx_post_load(void *opaque, int version_id) TCXState *s = opaque; update_palette_entries(s, 0, 256); - tcx_set_dirty(s); + tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); return 0; } @@ -699,7 +525,7 @@ static void tcx_stip_writel(void *opaque, hwaddr addr, val <<= 1; } } - memory_region_set_dirty(&s->vram_mem, addr, 32); + tcx_set_dirty(s, addr, 32); } } @@ -732,7 +558,7 @@ static void tcx_rstip_writel(void *opaque, hwaddr addr, val <<= 1; } } - memory_region_set_dirty(&s->vram_mem, addr, 32); + tcx_set_dirty(s, addr, 32); } } @@ -790,7 +616,7 @@ static void tcx_blit_writel(void *opaque, hwaddr addr, memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); } } - memory_region_set_dirty(&s->vram_mem, addr, len); + tcx_set_dirty(s, addr, len); } } @@ -824,7 +650,7 @@ static void tcx_rblit_writel(void *opaque, hwaddr addr, memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4); } } - memory_region_set_dirty(&s->vram_mem, addr, len); + tcx_set_dirty(s, addr, len); } } @@ -861,7 +687,7 @@ static void tcx_invalidate_cursor_position(TCXState *s) start = ymin * 1024; end = ymax * 1024; - memory_region_set_dirty(&s->vram_mem, start, end-start); + tcx_set_dirty(s, start, end - start); } static uint64_t tcx_thc_readl(void *opaque, hwaddr addr, @@ -1017,8 +843,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) vmstate_register_ram_global(&s->rom); fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); if (fcode_filename) { - ret = load_image_targphys(fcode_filename, s->prom_addr, - FCODE_MAX_ROM_SIZE); + ret = load_image_mr(fcode_filename, &s->rom); g_free(fcode_filename); if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); @@ -1076,7 +901,6 @@ static Property tcx_properties[] = { DEFINE_PROP_UINT16("width", TCXState, width, -1), DEFINE_PROP_UINT16("height", TCXState, height, -1), DEFINE_PROP_UINT16("depth", TCXState, depth, -1), - DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga.c b/hw/display/vga.c index 69c3e1d674..b2516c8d21 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1434,6 +1434,14 @@ void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2) } } +static bool vga_scanline_invalidated(VGACommonState *s, int y) +{ + if (y >= VGA_MAX_HEIGHT) { + return false; + } + return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f)); +} + void vga_sync_dirty_bitmap(VGACommonState *s) { memory_region_sync_dirty_bitmap(&s->vram); @@ -1457,7 +1465,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) DisplaySurface *surface = qemu_console_surface(s->con); int y1, y, update, linesize, y_start, double_scan, mask, depth; int width, height, shift_control, line_offset, bwidth, bits; - ram_addr_t page0, page1, page_min, page_max; + ram_addr_t page0, page1; + DirtyBitmapSnapshot *snap = NULL; int disp_width, multi_scan, multi_run; uint8_t *d; uint32_t v, addr1, addr; @@ -1472,9 +1481,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) full_update |= update_basic_params(s); - if (!full_update) - vga_sync_dirty_bitmap(s); - s->get_resolution(s, &width, &height); disp_width = width; @@ -1617,11 +1623,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) addr1 = (s->start_addr * 4); bwidth = (width * bits + 7) / 8; y_start = -1; - page_min = -1; - page_max = 0; d = surface_data(surface); linesize = surface_stride(surface); y1 = 0; + + if (!full_update) { + vga_sync_dirty_bitmap(s); + snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1, + bwidth * height, + DIRTY_MEMORY_VGA); + } + for(y = 0; y < height; y++) { addr = addr1; if (!(s->cr[VGA_CRTC_MODE] & 1)) { @@ -1636,17 +1648,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) update = full_update; page0 = addr; page1 = addr + bwidth - 1; - update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, - DIRTY_MEMORY_VGA); - /* explicit invalidation for the hardware cursor */ - update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; + if (full_update) { + update = 1; + } else { + update = memory_region_snapshot_get_dirty(&s->vram, snap, + page0, page1 - page0); + } + /* explicit invalidation for the hardware cursor (cirrus only) */ + update |= vga_scanline_invalidated(s, y); if (update) { if (y_start < 0) y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; if (!(is_buffer_shared(surface))) { vga_draw_line(s, d, s->vram_ptr + addr, width); if (s->cursor_draw_line) @@ -1679,14 +1691,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) dpy_gfx_update(s->con, 0, y_start, disp_width, y - y_start); } - /* reset modified pages */ - if (page_max >= page_min) { - memory_region_reset_dirty(&s->vram, - page_min, - page_max - page_min, - DIRTY_MEMORY_VGA); - } - memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); + g_free(snap); + memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table)); } static void vga_draw_blank(VGACommonState *s, int full_update) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 9b530ab5b0..e1056f34df 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -258,41 +258,22 @@ void virtio_gpu_get_display_info(VirtIOGPU *g, static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format) { switch (virtio_gpu_format) { -#ifdef HOST_WORDS_BIGENDIAN case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: - return PIXMAN_b8g8r8x8; + return PIXMAN_BE_b8g8r8x8; case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: - return PIXMAN_b8g8r8a8; + return PIXMAN_BE_b8g8r8a8; case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: - return PIXMAN_x8r8g8b8; + return PIXMAN_BE_x8r8g8b8; case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: - return PIXMAN_a8r8g8b8; + return PIXMAN_BE_a8r8g8b8; case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: - return PIXMAN_r8g8b8x8; + return PIXMAN_BE_r8g8b8x8; case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: - return PIXMAN_r8g8b8a8; + return PIXMAN_BE_r8g8b8a8; case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: - return PIXMAN_x8b8g8r8; + return PIXMAN_BE_x8b8g8r8; case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: - return PIXMAN_a8b8g8r8; -#else - case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: - return PIXMAN_x8r8g8b8; - case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: - return PIXMAN_a8r8g8b8; - case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: - return PIXMAN_b8g8r8x8; - case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: - return PIXMAN_b8g8r8a8; - case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: - return PIXMAN_x8b8g8r8; - case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: - return PIXMAN_a8b8g8r8; - case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: - return PIXMAN_r8g8b8x8; - case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: - return PIXMAN_r8g8b8a8; -#endif + return PIXMAN_BE_a8b8g8r8; default: return 0; } @@ -1170,8 +1151,8 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, g->config_size); - g->req_state[0].width = 1024; - g->req_state[0].height = 768; + g->req_state[0].width = g->conf.xres; + g->req_state[0].height = g->conf.yres; if (virtio_gpu_virgl_enabled(g->conf)) { /* use larger control queue in 3d mode */ @@ -1291,6 +1272,8 @@ static Property virtio_gpu_properties[] = { DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), #endif + DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024), + DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 6599cf078d..ec5f27d67e 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1118,9 +1118,9 @@ static void vmsvga_update_display(void *opaque) { struct vmsvga_state_s *s = opaque; DisplaySurface *surface; - bool dirty = false; - if (!s->enable) { + if (!s->enable || !s->config) { + /* in standard vga mode */ s->vga.hw_ops->gfx_update(&s->vga); return; } @@ -1131,26 +1131,11 @@ static void vmsvga_update_display(void *opaque) vmsvga_fifo_run(s); vmsvga_update_rect_flush(s); - /* - * Is it more efficient to look at vram VGA-dirty bits or wait - * for the driver to issue SVGA_CMD_UPDATE? - */ - if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) { - vga_sync_dirty_bitmap(&s->vga); - dirty = memory_region_get_dirty(&s->vga.vram, 0, - surface_stride(surface) * surface_height(surface), - DIRTY_MEMORY_VGA); - } - if (s->invalidated || dirty) { + if (s->invalidated) { s->invalidated = 0; dpy_gfx_update(s->vga.con, 0, 0, surface_width(surface), surface_height(surface)); } - if (dirty) { - memory_region_reset_dirty(&s->vga.vram, 0, - surface_stride(surface) * surface_height(surface), - DIRTY_MEMORY_VGA); - } } static void vmsvga_reset(DeviceState *dev) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index e0732ccaf1..f86a40aa30 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -572,8 +572,7 @@ static uint64_t amdvi_mmio_read(void *opaque, hwaddr addr, unsigned size) uint64_t val = -1; if (addr + size > AMDVI_MMIO_SIZE) { - trace_amdvi_mmio_read("error: addr outside region: max ", - (uint64_t)AMDVI_MMIO_SIZE, addr, size); + trace_amdvi_mmio_read_invalid(AMDVI_MMIO_SIZE, addr, size); return (uint64_t)-1; } diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 22d8226e43..02f047c8e3 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -595,6 +595,22 @@ static inline uint32_t vtd_get_agaw_from_context_entry(VTDContextEntry *ce) return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9; } +static inline uint64_t vtd_iova_limit(VTDContextEntry *ce) +{ + uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce); + return 1ULL << MIN(ce_agaw, VTD_MGAW); +} + +/* Return true if IOVA passes range check, otherwise false. */ +static inline bool vtd_iova_range_check(uint64_t iova, VTDContextEntry *ce) +{ + /* + * Check if @iova is above 2^X-1, where X is the minimum of MGAW + * in CAP_REG and AW in context-entry. + */ + return !(iova & ~(vtd_iova_limit(ce) - 1)); +} + static const uint64_t vtd_paging_entry_rsvd_field[] = { [0] = ~0ULL, /* For not large page */ @@ -630,13 +646,9 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, uint32_t level = vtd_get_level_from_context_entry(ce); uint32_t offset; uint64_t slpte; - uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce); uint64_t access_right_check; - /* Check if @iova is above 2^X-1, where X is the minimum of MGAW - * in CAP_REG and AW in context-entry. - */ - if (iova & ~((1ULL << MIN(ce_agaw, VTD_MGAW)) - 1)) { + if (!vtd_iova_range_check(iova, ce)) { VTD_DPRINTF(GENERAL, "error: iova 0x%"PRIx64 " exceeds limits", iova); return -VTD_FR_ADDR_BEYOND_MGAW; } @@ -684,6 +696,135 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, } } +typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); + +/** + * vtd_page_walk_level - walk over specific level for IOVA range + * + * @addr: base GPA addr to start the walk + * @start: IOVA range start address + * @end: IOVA range end address (start <= addr < end) + * @hook_fn: hook func to be called when detected page + * @private: private data to be passed into hook func + * @read: whether parent level has read permission + * @write: whether parent level has write permission + * @notify_unmap: whether we should notify invalid entries + */ +static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, + uint64_t end, vtd_page_walk_hook hook_fn, + void *private, uint32_t level, + bool read, bool write, bool notify_unmap) +{ + bool read_cur, write_cur, entry_valid; + uint32_t offset; + uint64_t slpte; + uint64_t subpage_size, subpage_mask; + IOMMUTLBEntry entry; + uint64_t iova = start; + uint64_t iova_next; + int ret = 0; + + trace_vtd_page_walk_level(addr, level, start, end); + + subpage_size = 1ULL << vtd_slpt_level_shift(level); + subpage_mask = vtd_slpt_level_page_mask(level); + + while (iova < end) { + iova_next = (iova & subpage_mask) + subpage_size; + + offset = vtd_iova_level_offset(iova, level); + slpte = vtd_get_slpte(addr, offset); + + if (slpte == (uint64_t)-1) { + trace_vtd_page_walk_skip_read(iova, iova_next); + goto next; + } + + if (vtd_slpte_nonzero_rsvd(slpte, level)) { + trace_vtd_page_walk_skip_reserve(iova, iova_next); + goto next; + } + + /* Permissions are stacked with parents' */ + read_cur = read && (slpte & VTD_SL_R); + write_cur = write && (slpte & VTD_SL_W); + + /* + * As long as we have either read/write permission, this is a + * valid entry. The rule works for both page entries and page + * table entries. + */ + entry_valid = read_cur | write_cur; + + if (vtd_is_last_slpte(slpte, level)) { + entry.target_as = &address_space_memory; + entry.iova = iova & subpage_mask; + /* NOTE: this is only meaningful if entry_valid == true */ + entry.translated_addr = vtd_get_slpte_addr(slpte); + entry.addr_mask = ~subpage_mask; + entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); + if (!entry_valid && !notify_unmap) { + trace_vtd_page_walk_skip_perm(iova, iova_next); + goto next; + } + trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr, + entry.addr_mask, entry.perm); + if (hook_fn) { + ret = hook_fn(&entry, private); + if (ret < 0) { + return ret; + } + } + } else { + if (!entry_valid) { + trace_vtd_page_walk_skip_perm(iova, iova_next); + goto next; + } + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte), iova, + MIN(iova_next, end), hook_fn, private, + level - 1, read_cur, write_cur, + notify_unmap); + if (ret < 0) { + return ret; + } + } + +next: + iova = iova_next; + } + + return 0; +} + +/** + * vtd_page_walk - walk specific IOVA range, and call the hook + * + * @ce: context entry to walk upon + * @start: IOVA address to start the walk + * @end: IOVA range end address (start <= addr < end) + * @hook_fn: the hook that to be called for each detected area + * @private: private data for the hook function + */ +static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, + vtd_page_walk_hook hook_fn, void *private, + bool notify_unmap) +{ + dma_addr_t addr = vtd_get_slpt_base_from_context(ce); + uint32_t level = vtd_get_level_from_context_entry(ce); + + if (!vtd_iova_range_check(start, ce)) { + return -VTD_FR_ADDR_BEYOND_MGAW; + } + + if (!vtd_iova_range_check(end, ce)) { + /* Fix end so that it reaches the maximum */ + end = vtd_iova_limit(ce); + } + + return vtd_page_walk_level(addr, start, end, hook_fn, private, + level, true, true, notify_unmap); +} + /* Map a device to its corresponding domain (context-entry) */ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, uint8_t devfn, VTDContextEntry *ce) @@ -898,6 +1039,15 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) s->intr_root, s->intr_size); } +static void vtd_iommu_replay_all(IntelIOMMUState *s) +{ + IntelIOMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + memory_region_iommu_replay_all(&node->vtd_as->iommu); + } +} + static void vtd_context_global_invalidate(IntelIOMMUState *s) { trace_vtd_inv_desc_cc_global(); @@ -905,6 +1055,14 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s) if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { vtd_reset_context_cache(s); } + /* + * From VT-d spec 6.5.2.1, a global context entry invalidation + * should be followed by a IOTLB global invalidation, so we should + * be safe even without this. Hoewever, let's replay the region as + * well to be safer, and go back here when we need finer tunes for + * VT-d emulation codes. + */ + vtd_iommu_replay_all(s); } @@ -971,6 +1129,16 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), VTD_PCI_FUNC(devfn_it)); vtd_as->context_cache_entry.context_cache_gen = 0; + /* + * So a device is moving out of (or moving into) a + * domain, a replay() suites here to notify all the + * IOMMU_NOTIFIER_MAP registers about this change. + * This won't bring bad even if we have no such + * notifier registered - the IOMMU notification + * framework will skip MAP notifications if that + * happened. + */ + memory_region_iommu_replay_all(&vtd_as->iommu); } } } @@ -1012,12 +1180,53 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) { trace_vtd_iotlb_reset("global invalidation recved"); vtd_reset_iotlb(s); + vtd_iommu_replay_all(s); } static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) { + IntelIOMMUNotifierNode *node; + VTDContextEntry ce; + VTDAddressSpace *vtd_as; + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); + + QLIST_FOREACH(node, &s->notifiers_list, next) { + vtd_as = node->vtd_as; + if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce) && + domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + memory_region_iommu_replay_all(&vtd_as->iommu); + } + } +} + +static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, + void *private) +{ + memory_region_notify_iommu((MemoryRegion *)private, *entry); + return 0; +} + +static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, + uint16_t domain_id, hwaddr addr, + uint8_t am) +{ + IntelIOMMUNotifierNode *node; + VTDContextEntry ce; + int ret; + + QLIST_FOREACH(node, &(s->notifiers_list), next) { + VTDAddressSpace *vtd_as = node->vtd_as; + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce); + if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE, + vtd_page_invalidate_notify_hook, + (void *)&vtd_as->iommu, true); + } + } } static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, @@ -1030,6 +1239,7 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, info.addr = addr; info.mask = ~((1 << am) - 1); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); + vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); } /* Flush IOTLB @@ -1151,9 +1361,49 @@ static void vtd_handle_gcmd_sirtp(IntelIOMMUState *s) vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_IRTPS); } +static void vtd_switch_address_space(VTDAddressSpace *as) +{ + assert(as); + + trace_vtd_switch_address_space(pci_bus_num(as->bus), + VTD_PCI_SLOT(as->devfn), + VTD_PCI_FUNC(as->devfn), + as->iommu_state->dmar_enabled); + + /* Turn off first then on the other */ + if (as->iommu_state->dmar_enabled) { + memory_region_set_enabled(&as->sys_alias, false); + memory_region_set_enabled(&as->iommu, true); + } else { + memory_region_set_enabled(&as->iommu, false); + memory_region_set_enabled(&as->sys_alias, true); + } +} + +static void vtd_switch_address_space_all(IntelIOMMUState *s) +{ + GHashTableIter iter; + VTDBus *vtd_bus; + int i; + + g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { + for (i = 0; i < X86_IOMMU_PCI_DEVFN_MAX; i++) { + if (!vtd_bus->dev_as[i]) { + continue; + } + vtd_switch_address_space(vtd_bus->dev_as[i]); + } + } +} + /* Handle Translation Enable/Disable */ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) { + if (s->dmar_enabled == en) { + return; + } + VTD_DPRINTF(CSR, "Translation Enable %s", (en ? "on" : "off")); if (en) { @@ -1168,6 +1418,8 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) /* Ok - report back to driver */ vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0); } + + vtd_switch_address_space_all(s); } /* Handle Interrupt Remap Enable/Disable */ @@ -1457,7 +1709,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, entry.iova = addr; entry.perm = IOMMU_NONE; entry.translated_addr = 0; - memory_region_notify_iommu(entry.target_as->root, entry); + memory_region_notify_iommu(&vtd_dev_as->iommu, entry); done: return true; @@ -2005,15 +2257,33 @@ static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu, IOMMUNotifierFlag new) { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); + IntelIOMMUState *s = vtd_as->iommu_state; + IntelIOMMUNotifierNode *node = NULL; + IntelIOMMUNotifierNode *next_node = NULL; - if (new & IOMMU_NOTIFIER_MAP) { - error_report("Device at bus %s addr %02x.%d requires iommu " - "notifier which is currently not supported by " - "intel-iommu emulation", - vtd_as->bus->qbus.name, PCI_SLOT(vtd_as->devfn), - PCI_FUNC(vtd_as->devfn)); + if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { + error_report("We need to set cache_mode=1 for intel-iommu to enable " + "device assignment with IOMMU protection."); exit(1); } + + if (old == IOMMU_NOTIFIER_NONE) { + node = g_malloc0(sizeof(*node)); + node->vtd_as = vtd_as; + QLIST_INSERT_HEAD(&s->notifiers_list, node, next); + return; + } + + /* update notifier node with new flags */ + QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { + if (node->vtd_as == vtd_as) { + if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(node, next); + g_free(node); + } + return; + } + } } static const VMStateDescription vtd_vmstate = { @@ -2389,19 +2659,150 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->devfn = (uint8_t)devfn; vtd_dev_as->iommu_state = s; vtd_dev_as->context_cache_entry.context_cache_gen = 0; + + /* + * Memory region relationships looks like (Address range shows + * only lower 32 bits to make it short in length...): + * + * |-----------------+-------------------+----------| + * | Name | Address range | Priority | + * |-----------------+-------------------+----------+ + * | vtd_root | 00000000-ffffffff | 0 | + * | intel_iommu | 00000000-ffffffff | 1 | + * | vtd_sys_alias | 00000000-ffffffff | 1 | + * | intel_iommu_ir | fee00000-feefffff | 64 | + * |-----------------+-------------------+----------| + * + * We enable/disable DMAR by switching enablement for + * vtd_sys_alias and intel_iommu regions. IR region is always + * enabled. + */ memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s), - &s->iommu_ops, "intel_iommu", UINT64_MAX); + &s->iommu_ops, "intel_iommu_dmar", + UINT64_MAX); + memory_region_init_alias(&vtd_dev_as->sys_alias, OBJECT(s), + "vtd_sys_alias", get_system_memory(), + 0, memory_region_size(get_system_memory())); memory_region_init_io(&vtd_dev_as->iommu_ir, OBJECT(s), &vtd_mem_ir_ops, s, "intel_iommu_ir", VTD_INTERRUPT_ADDR_SIZE); - memory_region_add_subregion(&vtd_dev_as->iommu, VTD_INTERRUPT_ADDR_FIRST, - &vtd_dev_as->iommu_ir); - address_space_init(&vtd_dev_as->as, - &vtd_dev_as->iommu, name); + memory_region_init(&vtd_dev_as->root, OBJECT(s), + "vtd_root", UINT64_MAX); + memory_region_add_subregion_overlap(&vtd_dev_as->root, + VTD_INTERRUPT_ADDR_FIRST, + &vtd_dev_as->iommu_ir, 64); + address_space_init(&vtd_dev_as->as, &vtd_dev_as->root, name); + memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, + &vtd_dev_as->sys_alias, 1); + memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, + &vtd_dev_as->iommu, 1); + vtd_switch_address_space(vtd_dev_as); } return vtd_dev_as; } +/* Unmap the whole range in the notifier's scope. */ +static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) +{ + IOMMUTLBEntry entry; + hwaddr size; + hwaddr start = n->start; + hwaddr end = n->end; + + /* + * Note: all the codes in this function has a assumption that IOVA + * bits are no more than VTD_MGAW bits (which is restricted by + * VT-d spec), otherwise we need to consider overflow of 64 bits. + */ + + if (end > VTD_ADDRESS_SIZE) { + /* + * Don't need to unmap regions that is bigger than the whole + * VT-d supported address space size + */ + end = VTD_ADDRESS_SIZE; + } + + assert(start <= end); + size = end - start; + + if (ctpop64(size) != 1) { + /* + * This size cannot format a correct mask. Let's enlarge it to + * suite the minimum available mask. + */ + int n = 64 - clz64(size); + if (n > VTD_MGAW) { + /* should not happen, but in case it happens, limit it */ + n = VTD_MGAW; + } + size = 1ULL << n; + } + + entry.target_as = &address_space_memory; + /* Adjust iova for the size */ + entry.iova = n->start & ~(size - 1); + /* This field is meaningless for unmap */ + entry.translated_addr = 0; + entry.perm = IOMMU_NONE; + entry.addr_mask = size - 1; + + trace_vtd_as_unmap_whole(pci_bus_num(as->bus), + VTD_PCI_SLOT(as->devfn), + VTD_PCI_FUNC(as->devfn), + entry.iova, size); + + memory_region_notify_one(n, &entry); +} + +static void vtd_address_space_unmap_all(IntelIOMMUState *s) +{ + IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; + IOMMUNotifier *n; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + vtd_as = node->vtd_as; + IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { + vtd_address_space_unmap(vtd_as, n); + } + } +} + +static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) +{ + memory_region_notify_one((IOMMUNotifier *)private, entry); + return 0; +} + +static void vtd_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) +{ + VTDAddressSpace *vtd_as = container_of(mr, VTDAddressSpace, iommu); + IntelIOMMUState *s = vtd_as->iommu_state; + uint8_t bus_n = pci_bus_num(vtd_as->bus); + VTDContextEntry ce; + + /* + * The replay can be triggered by either a invalidation or a newly + * created entry. No matter what, we release existing mappings + * (it means flushing caches for UNMAP-only registers). + */ + vtd_address_space_unmap(vtd_as, n); + + if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) { + trace_vtd_replay_ce_valid(bus_n, PCI_SLOT(vtd_as->devfn), + PCI_FUNC(vtd_as->devfn), + VTD_CONTEXT_ENTRY_DID(ce.hi), + ce.hi, ce.lo); + vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false); + } else { + trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), + PCI_FUNC(vtd_as->devfn)); + } + + return; +} + /* Do the initialization. It will also be called when reset, so pay * attention when adding new initialization stuff. */ @@ -2416,6 +2817,7 @@ static void vtd_init(IntelIOMMUState *s) s->iommu_ops.translate = vtd_iommu_translate; s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed; + s->iommu_ops.replay = vtd_iommu_replay; s->root = 0; s->root_extended = false; s->dmar_enabled = false; @@ -2511,6 +2913,11 @@ static void vtd_reset(DeviceState *dev) VTD_DPRINTF(GENERAL, ""); vtd_init(s); + + /* + * When device reset, throw away all mappings and external caches + */ + vtd_address_space_unmap_all(s); } static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) @@ -2574,6 +2981,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) return; } + QLIST_INIT(&s->notifiers_list); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 41041219ba..29d67075f4 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -197,6 +197,7 @@ #define VTD_DOMAIN_ID_MASK ((1UL << VTD_DOMAIN_ID_SHIFT) - 1) #define VTD_CAP_ND (((VTD_DOMAIN_ID_SHIFT - 4) / 2) & 7ULL) #define VTD_MGAW 39 /* Maximum Guest Address Width */ +#define VTD_ADDRESS_SIZE (1ULL << VTD_MGAW) #define VTD_CAP_MGAW (((VTD_MGAW - 1) & 0x3fULL) << 16) #define VTD_MAMV 18ULL #define VTD_CAP_MAMV (VTD_MAMV << 48) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d24388e05f..f3b372a18f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1104,9 +1104,7 @@ static void pc_new_cpu(const char *typename, int64_t apic_id, Error **errp) object_property_set_bool(cpu, true, "realized", &local_err); object_unref(cpu); - if (local_err) { - error_propagate(errp, local_err); - } + error_propagate(errp, local_err); } void pc_hot_add_cpu(const int64_t id, Error **errp) diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 88ad5e4c43..04a6980800 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -4,7 +4,6 @@ x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32 # hw/i386/intel_iommu.c -vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_inv_desc(const char *type, uint64_t hi, uint64_t lo) "invalidate desc type %s high 0x%"PRIx64" low 0x%"PRIx64 vtd_inv_desc_invalid(uint64_t hi, uint64_t lo) "invalid inv desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_cc_domain(uint16_t domain) "context invalidate domain 0x%"PRIx16 @@ -30,6 +29,15 @@ vtd_iotlb_cc_hit(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32 vtd_iotlb_cc_update(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32_t gen1, uint32_t gen2) "IOTLB context update bus 0x%"PRIx8" devfn 0x%"PRIx8" high 0x%"PRIx64" low 0x%"PRIx64" gen %"PRIu32" -> gen %"PRIu32 vtd_iotlb_reset(const char *reason) "IOTLB reset (reason: %s)" vtd_fault_disabled(void) "Fault processing disabled for context entry" +vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 +vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 +vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 +vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" +vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" +vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" +vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" +vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 # hw/i386/amd_iommu.c amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32 @@ -37,6 +45,7 @@ amdvi_cache_update(uint16_t domid, uint8_t bus, uint8_t slot, uint8_t func, uint amdvi_completion_wait_fail(uint64_t addr) "error: fail to write at address 0x%"PRIx64 amdvi_mmio_write(const char *reg, uint64_t addr, unsigned size, uint64_t val, uint64_t offset) "%s write addr 0x%"PRIx64", size %u, val 0x%"PRIx64", offset 0x%"PRIx64 amdvi_mmio_read(const char *reg, uint64_t addr, unsigned size, uint64_t offset) "%s read addr 0x%"PRIx64", size %u offset 0x%"PRIx64 +amdvi_mmio_read_invalid(int max, uint64_t addr, unsigned size) "error: addr outside region (max 0x%x): read addr 0x%" PRIx64 ", size %u" amdvi_command_error(uint64_t status) "error: Executing commands with command buffer disabled 0x%"PRIx64 amdvi_command_read_fail(uint64_t addr, uint32_t head) "error: fail to access memory at 0x%"PRIx64" + 0x%"PRIx32 amdvi_command_exec(uint32_t head, uint32_t tail, uint64_t buf) "command buffer head at 0x%"PRIx32" command buffer tail at 0x%"PRIx32" command buffer base at 0x%"PRIx64 diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index b678ee9f20..0e42f0d02c 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -22,7 +22,6 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) { VirtQueueElement *elem; - unsigned have, need; int i, len; if (!vinput->active) { @@ -32,10 +31,10 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) /* queue up events ... */ if (vinput->qindex == vinput->qsize) { vinput->qsize++; - vinput->queue = realloc(vinput->queue, vinput->qsize * - sizeof(virtio_input_event)); + vinput->queue = g_realloc(vinput->queue, vinput->qsize * + sizeof(vinput->queue[0])); } - vinput->queue[vinput->qindex++] = *event; + vinput->queue[vinput->qindex++].event = *event; /* ... until we see a report sync ... */ if (event->type != cpu_to_le16(EV_SYN) || @@ -44,24 +43,24 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) } /* ... then check available space ... */ - need = sizeof(virtio_input_event) * vinput->qindex; - virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0); - if (have < need) { - vinput->qindex = 0; - trace_virtio_input_queue_full(); - return; - } - - /* ... and finally pass them to the guest */ for (i = 0; i < vinput->qindex; i++) { elem = virtqueue_pop(vinput->evt, sizeof(VirtQueueElement)); if (!elem) { - /* should not happen, we've checked for space beforehand */ - fprintf(stderr, "%s: Huh? No vq elem available ...\n", __func__); + while (--i >= 0) { + virtqueue_unpop(vinput->evt, vinput->queue[i].elem, 0); + } + vinput->qindex = 0; + trace_virtio_input_queue_full(); return; } + vinput->queue[i].elem = elem; + } + + /* ... and finally pass them to the guest */ + for (i = 0; i < vinput->qindex; i++) { + elem = vinput->queue[i].elem; len = iov_from_buf(elem->in_sg, elem->in_num, - 0, vinput->queue+i, sizeof(virtio_input_event)); + 0, &vinput->queue[i].event, sizeof(virtio_input_event)); virtqueue_push(vinput->evt, elem, len); g_free(elem); } @@ -272,6 +271,8 @@ static void virtio_input_finalize(Object *obj) QTAILQ_REMOVE(&vinput->cfg_list, cfg, node); g_free(cfg); } + + g_free(vinput->queue); } static void virtio_input_device_unrealize(DeviceState *dev, Error **errp) { diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 7a6e771ed1..c3829e31b5 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -387,25 +387,6 @@ static bool apic_common_sipi_needed(void *opaque) return s->wait_for_sipi != 0; } -static bool apic_irq_delivered_needed(void *opaque) -{ - APICCommonState *s = APIC_COMMON(opaque); - return s->cpu == X86_CPU(first_cpu) && apic_irq_delivered != 0; -} - -static void apic_irq_delivered_pre_save(void *opaque) -{ - APICCommonState *s = APIC_COMMON(opaque); - s->apic_irq_delivered = apic_irq_delivered; -} - -static int apic_irq_delivered_post_load(void *opaque, int version_id) -{ - APICCommonState *s = APIC_COMMON(opaque); - apic_irq_delivered = s->apic_irq_delivered; - return 0; -} - static const VMStateDescription vmstate_apic_common_sipi = { .name = "apic_sipi", .version_id = 1, @@ -418,19 +399,6 @@ static const VMStateDescription vmstate_apic_common_sipi = { } }; -static const VMStateDescription vmstate_apic_irq_delivered = { - .name = "apic_irq_delivered", - .version_id = 1, - .minimum_version_id = 1, - .needed = apic_irq_delivered_needed, - .pre_save = apic_irq_delivered_pre_save, - .post_load = apic_irq_delivered_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(apic_irq_delivered, APICCommonState), - VMSTATE_END_OF_LIST() - } -}; - static const VMStateDescription vmstate_apic_common = { .name = "apic", .version_id = 3, @@ -465,7 +433,6 @@ static const VMStateDescription vmstate_apic_common = { }, .subsections = (const VMStateDescription*[]) { &vmstate_apic_common_sipi, - &vmstate_apic_irq_delivered, NULL } }; diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 81f0403117..19aab56072 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -614,12 +614,6 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) s = c->gic; cpu = ARM_CPU(c->cpu); - /* Initialize to actual HW supported configuration */ - kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, - KVM_VGIC_ATTR(ICC_CTLR_EL1, cpu->mp_affinity), - &c->icc_ctlr_el1[GICV3_NS], false); - - c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS]; c->icc_pmr_el1 = 0; c->icc_bpr[GICV3_G0] = GIC_MIN_BPR; c->icc_bpr[GICV3_G1] = GIC_MIN_BPR; @@ -628,6 +622,17 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) c->icc_sre_el1 = 0x7; memset(c->icc_apr, 0, sizeof(c->icc_apr)); memset(c->icc_igrpen, 0, sizeof(c->icc_igrpen)); + + if (s->migration_blocker) { + return; + } + + /* Initialize to actual HW supported configuration */ + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + KVM_VGIC_ATTR(ICC_CTLR_EL1, cpu->mp_affinity), + &c->icc_ctlr_el1[GICV3_NS], false); + + c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS]; } static void kvm_arm_gicv3_reset(DeviceState *dev) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index bef4caf980..711c11454f 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -21,11 +21,14 @@ S390FLICState *s390_get_flic(void) { - S390FLICState *fs; + static S390FLICState *fs; - fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); if (!fs) { - fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL)); + fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); + if (!fs) { + fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, + NULL)); + } } return fs; } diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 1c69cb33f8..2fcc3d2e7c 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -37,40 +37,30 @@ #define IPMI_BT_HBUSY_BIT 6 #define IPMI_BT_BBUSY_BIT 7 -#define IPMI_BT_CLR_WR_MASK (1 << IPMI_BT_CLR_WR_BIT) #define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) -#define IPMI_BT_SET_CLR_WR(d, v) (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \ - (((v & 1) << IPMI_BT_CLR_WR_BIT))) -#define IPMI_BT_CLR_RD_MASK (1 << IPMI_BT_CLR_RD_BIT) #define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) -#define IPMI_BT_SET_CLR_RD(d, v) (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \ - (((v & 1) << IPMI_BT_CLR_RD_BIT))) -#define IPMI_BT_H2B_ATN_MASK (1 << IPMI_BT_H2B_ATN_BIT) #define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) -#define IPMI_BT_SET_H2B_ATN(d, v) (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \ - (((v & 1) << IPMI_BT_H2B_ATN_BIT))) #define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) #define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) -#define IPMI_BT_SET_B2H_ATN(d, v) (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ - (((v & 1) << IPMI_BT_B2H_ATN_BIT))) +#define IPMI_BT_SET_B2H_ATN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ + (((v) & 1) << IPMI_BT_B2H_ATN_BIT))) #define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) #define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) -#define IPMI_BT_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ - (((v & 1) << IPMI_BT_SMS_ATN_BIT))) +#define IPMI_BT_SET_SMS_ATN(d, v) ((d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ + (((v) & 1) << IPMI_BT_SMS_ATN_BIT))) #define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) #define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) -#define IPMI_BT_SET_HBUSY(d, v) (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ - (((v & 1) << IPMI_BT_HBUSY_BIT))) +#define IPMI_BT_SET_HBUSY(d, v) ((d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ + (((v) & 1) << IPMI_BT_HBUSY_BIT))) #define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) -#define IPMI_BT_GET_BBUSY(d) (((d) >> IPMI_BT_BBUSY_BIT) & 0x1) -#define IPMI_BT_SET_BBUSY(d, v) (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ - (((v & 1) << IPMI_BT_BBUSY_BIT))) +#define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ + (((v) & 1) << IPMI_BT_BBUSY_BIT))) /* Mask register */ @@ -79,13 +69,13 @@ #define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) #define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) -#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \ - (((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT))) +#define IPMI_BT_SET_B2H_IRQ_EN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) |\ + (((v) & 1) << IPMI_BT_B2H_IRQ_EN_BIT))) #define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) #define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) -#define IPMI_BT_SET_B2H_IRQ(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ - (((v & 1) << IPMI_BT_B2H_IRQ_BIT))) +#define IPMI_BT_SET_B2H_IRQ(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ + (((v) & 1) << IPMI_BT_B2H_IRQ_BIT))) typedef struct IPMIBT { IPMIBmc *bmc; diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 59930dd9d0..a0866c3856 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -312,11 +312,6 @@ void ich9_generate_smi(void) cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); } -void ich9_generate_nmi(void) -{ - cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI); -} - static int ich9_lpc_sci_irq(ICH9LPCState *lpc) { switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] & diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index e30dbc7d3d..63a8ccd355 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -401,8 +401,8 @@ static uint64_t exynos4210_pmu_read(void *opaque, hwaddr offset, unsigned size) { Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; - unsigned i; const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; + unsigned int i; for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { if (reg_p->offset == offset) { @@ -420,8 +420,8 @@ static void exynos4210_pmu_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; - unsigned i; const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; + unsigned int i; for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { if (reg_p->offset == offset) { diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 6a95d92d37..5ddaffe63a 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -26,6 +26,7 @@ common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o common-obj-$(CONFIG_LANCE) += lance.o +common-obj-$(CONFIG_FTGMAC100) += ftgmac100.o obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o obj-$(CONFIG_COLDFIRE) += mcf_fec.o diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index d4de8ad9f1..3943187572 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -300,6 +300,8 @@ #define DESC_1_RX_SOF 0x00004000 #define DESC_1_RX_EOF 0x00008000 +#define GEM_MODID_VALUE 0x00020118 + static inline unsigned tx_desc_get_buffer(unsigned *desc) { return desc[0]; @@ -481,14 +483,17 @@ static int gem_can_receive(NetClientState *nc) } for (i = 0; i < s->num_priority_queues; i++) { - if (rx_desc_get_ownership(s->rx_desc[i]) == 1) { - if (s->can_rx_state != 2) { - s->can_rx_state = 2; - DB_PRINT("can't receive - busy buffer descriptor (q%d) 0x%x\n", - i, s->rx_desc_addr[i]); - } - return 0; + if (rx_desc_get_ownership(s->rx_desc[i]) != 1) { + break; + } + }; + + if (i == s->num_priority_queues) { + if (s->can_rx_state != 2) { + s->can_rx_state = 2; + DB_PRINT("can't receive - all the buffer descriptors are busy\n"); } + return 0; } if (s->can_rx_state != 0) { @@ -506,7 +511,18 @@ static void gem_update_int_status(CadenceGEMState *s) { int i; - if ((s->num_priority_queues == 1) && s->regs[GEM_ISR]) { + if (!s->regs[GEM_ISR]) { + /* ISR isn't set, clear all the interrupts */ + for (i = 0; i < s->num_priority_queues; ++i) { + qemu_set_irq(s->irq[i], 0); + } + return; + } + + /* If we get here we know s->regs[GEM_ISR] is set, so we don't need to + * check it again. + */ + if (s->num_priority_queues == 1) { /* No priority queues, just trigger the interrupt */ DB_PRINT("asserting int.\n"); qemu_set_irq(s->irq[0], 1); @@ -790,8 +806,8 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q) { DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr[q]); /* read current descriptor */ - cpu_physical_memory_read(s->rx_desc_addr[0], - (uint8_t *)s->rx_desc[0], sizeof(s->rx_desc[0])); + cpu_physical_memory_read(s->rx_desc_addr[q], + (uint8_t *)s->rx_desc[q], sizeof(s->rx_desc[q])); /* Descriptor owned by software ? */ if (rx_desc_get_ownership(s->rx_desc[q]) == 1) { @@ -1209,7 +1225,7 @@ static void gem_reset(DeviceState *d) s->regs[GEM_TXPAUSE] = 0x0000ffff; s->regs[GEM_TXPARTIALSF] = 0x000003ff; s->regs[GEM_RXPARTIALSF] = 0x000003ff; - s->regs[GEM_MODID] = 0x00020118; + s->regs[GEM_MODID] = s->revision; s->regs[GEM_DESCONF] = 0x02500111; s->regs[GEM_DESCONF2] = 0x2ab13fff; s->regs[GEM_DESCONF5] = 0x002f2145; @@ -1271,7 +1287,6 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) { CadenceGEMState *s; uint32_t retval; - int i; s = (CadenceGEMState *)opaque; offset >>= 2; @@ -1282,9 +1297,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) switch (offset) { case GEM_ISR: DB_PRINT("lowering irqs on ISR read\n"); - for (i = 0; i < s->num_priority_queues; ++i) { - qemu_set_irq(s->irq[i], 0); - } + /* The interrupts get updated at the end of the function. */ break; case GEM_PHYMNTNC: if (retval & GEM_PHYMNTNC_OP_R) { @@ -1508,6 +1521,8 @@ static const VMStateDescription vmstate_cadence_gem = { static Property gem_properties[] = { DEFINE_NIC_PROPERTIES(CadenceGEMState, conf), + DEFINE_PROP_UINT32("revision", CadenceGEMState, revision, + GEM_MODID_VALUE), DEFINE_PROP_UINT8("num-priority-queues", CadenceGEMState, num_priority_queues, 1), DEFINE_PROP_UINT8("num-type1-screeners", CadenceGEMState, diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 93249497f4..f2e5072d27 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -40,7 +40,7 @@ static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -#define E1000_DEBUG +/* #define E1000_DEBUG */ #ifdef E1000_DEBUG enum { diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c new file mode 100644 index 0000000000..3c36ab9cec --- /dev/null +++ b/hw/net/ftgmac100.c @@ -0,0 +1,1016 @@ +/* + * Faraday FTGMAC100 Gigabit Ethernet + * + * Copyright (C) 2016-2017, IBM Corporation. + * + * Based on Coldfire Fast Ethernet Controller emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/net/ftgmac100.h" +#include "sysemu/dma.h" +#include "qemu/log.h" +#include "net/checksum.h" +#include "net/eth.h" +#include "hw/net/mii.h" + +/* For crc32 */ +#include <zlib.h> + +/* + * FTGMAC100 registers + */ +#define FTGMAC100_ISR 0x00 +#define FTGMAC100_IER 0x04 +#define FTGMAC100_MAC_MADR 0x08 +#define FTGMAC100_MAC_LADR 0x0c +#define FTGMAC100_MATH0 0x10 +#define FTGMAC100_MATH1 0x14 +#define FTGMAC100_NPTXPD 0x18 +#define FTGMAC100_RXPD 0x1C +#define FTGMAC100_NPTXR_BADR 0x20 +#define FTGMAC100_RXR_BADR 0x24 +#define FTGMAC100_HPTXPD 0x28 +#define FTGMAC100_HPTXR_BADR 0x2c +#define FTGMAC100_ITC 0x30 +#define FTGMAC100_APTC 0x34 +#define FTGMAC100_DBLAC 0x38 +#define FTGMAC100_REVR 0x40 +#define FTGMAC100_FEAR1 0x44 +#define FTGMAC100_RBSR 0x4c +#define FTGMAC100_TPAFCR 0x48 + +#define FTGMAC100_MACCR 0x50 +#define FTGMAC100_MACSR 0x54 +#define FTGMAC100_PHYCR 0x60 +#define FTGMAC100_PHYDATA 0x64 +#define FTGMAC100_FCR 0x68 + +/* + * Interrupt status register & interrupt enable register + */ +#define FTGMAC100_INT_RPKT_BUF (1 << 0) +#define FTGMAC100_INT_RPKT_FIFO (1 << 1) +#define FTGMAC100_INT_NO_RXBUF (1 << 2) +#define FTGMAC100_INT_RPKT_LOST (1 << 3) +#define FTGMAC100_INT_XPKT_ETH (1 << 4) +#define FTGMAC100_INT_XPKT_FIFO (1 << 5) +#define FTGMAC100_INT_NO_NPTXBUF (1 << 6) +#define FTGMAC100_INT_XPKT_LOST (1 << 7) +#define FTGMAC100_INT_AHB_ERR (1 << 8) +#define FTGMAC100_INT_PHYSTS_CHG (1 << 9) +#define FTGMAC100_INT_NO_HPTXBUF (1 << 10) + +/* + * Automatic polling timer control register + */ +#define FTGMAC100_APTC_RXPOLL_CNT(x) ((x) & 0xf) +#define FTGMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) +#define FTGMAC100_APTC_TXPOLL_CNT(x) (((x) >> 8) & 0xf) +#define FTGMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) + +/* + * PHY control register + */ +#define FTGMAC100_PHYCR_MIIRD (1 << 26) +#define FTGMAC100_PHYCR_MIIWR (1 << 27) + +#define FTGMAC100_PHYCR_DEV(x) (((x) >> 16) & 0x1f) +#define FTGMAC100_PHYCR_REG(x) (((x) >> 21) & 0x1f) + +/* + * PHY data register + */ +#define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff) +#define FTGMAC100_PHYDATA_MIIRDATA(x) (((x) >> 16) & 0xffff) + +/* + * Feature Register + */ +#define FTGMAC100_REVR_NEW_MDIO_INTERFACE (1 << 31) + +/* + * MAC control register + */ +#define FTGMAC100_MACCR_TXDMA_EN (1 << 0) +#define FTGMAC100_MACCR_RXDMA_EN (1 << 1) +#define FTGMAC100_MACCR_TXMAC_EN (1 << 2) +#define FTGMAC100_MACCR_RXMAC_EN (1 << 3) +#define FTGMAC100_MACCR_RM_VLAN (1 << 4) +#define FTGMAC100_MACCR_HPTXR_EN (1 << 5) +#define FTGMAC100_MACCR_LOOP_EN (1 << 6) +#define FTGMAC100_MACCR_ENRX_IN_HALFTX (1 << 7) +#define FTGMAC100_MACCR_FULLDUP (1 << 8) +#define FTGMAC100_MACCR_GIGA_MODE (1 << 9) +#define FTGMAC100_MACCR_CRC_APD (1 << 10) /* not needed */ +#define FTGMAC100_MACCR_RX_RUNT (1 << 12) +#define FTGMAC100_MACCR_JUMBO_LF (1 << 13) +#define FTGMAC100_MACCR_RX_ALL (1 << 14) +#define FTGMAC100_MACCR_HT_MULTI_EN (1 << 15) +#define FTGMAC100_MACCR_RX_MULTIPKT (1 << 16) +#define FTGMAC100_MACCR_RX_BROADPKT (1 << 17) +#define FTGMAC100_MACCR_DISCARD_CRCERR (1 << 18) +#define FTGMAC100_MACCR_FAST_MODE (1 << 19) +#define FTGMAC100_MACCR_SW_RST (1 << 31) + +/* + * Transmit descriptor + */ +#define FTGMAC100_TXDES0_TXBUF_SIZE(x) ((x) & 0x3fff) +#define FTGMAC100_TXDES0_EDOTR (1 << 15) +#define FTGMAC100_TXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_TXDES0_LTS (1 << 28) +#define FTGMAC100_TXDES0_FTS (1 << 29) +#define FTGMAC100_TXDES0_EDOTR_ASPEED (1 << 30) +#define FTGMAC100_TXDES0_TXDMA_OWN (1 << 31) + +#define FTGMAC100_TXDES1_VLANTAG_CI(x) ((x) & 0xffff) +#define FTGMAC100_TXDES1_INS_VLANTAG (1 << 16) +#define FTGMAC100_TXDES1_TCP_CHKSUM (1 << 17) +#define FTGMAC100_TXDES1_UDP_CHKSUM (1 << 18) +#define FTGMAC100_TXDES1_IP_CHKSUM (1 << 19) +#define FTGMAC100_TXDES1_LLC (1 << 22) +#define FTGMAC100_TXDES1_TX2FIC (1 << 30) +#define FTGMAC100_TXDES1_TXIC (1 << 31) + +/* + * Receive descriptor + */ +#define FTGMAC100_RXDES0_VDBC 0x3fff +#define FTGMAC100_RXDES0_EDORR (1 << 15) +#define FTGMAC100_RXDES0_MULTICAST (1 << 16) +#define FTGMAC100_RXDES0_BROADCAST (1 << 17) +#define FTGMAC100_RXDES0_RX_ERR (1 << 18) +#define FTGMAC100_RXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_RXDES0_FTL (1 << 20) +#define FTGMAC100_RXDES0_RUNT (1 << 21) +#define FTGMAC100_RXDES0_RX_ODD_NB (1 << 22) +#define FTGMAC100_RXDES0_FIFO_FULL (1 << 23) +#define FTGMAC100_RXDES0_PAUSE_OPCODE (1 << 24) +#define FTGMAC100_RXDES0_PAUSE_FRAME (1 << 25) +#define FTGMAC100_RXDES0_LRS (1 << 28) +#define FTGMAC100_RXDES0_FRS (1 << 29) +#define FTGMAC100_RXDES0_EDORR_ASPEED (1 << 30) +#define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31) + +#define FTGMAC100_RXDES1_VLANTAG_CI 0xffff +#define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20) +#define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20) +#define FTGMAC100_RXDES1_PROT_IP (0x1 << 20) +#define FTGMAC100_RXDES1_PROT_TCPIP (0x2 << 20) +#define FTGMAC100_RXDES1_PROT_UDPIP (0x3 << 20) +#define FTGMAC100_RXDES1_LLC (1 << 22) +#define FTGMAC100_RXDES1_DF (1 << 23) +#define FTGMAC100_RXDES1_VLANTAG_AVAIL (1 << 24) +#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR (1 << 25) +#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) +#define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) + +/* + * Receive and transmit Buffer Descriptor + */ +typedef struct { + uint32_t des0; + uint32_t des1; + uint32_t des2; /* not used by HW */ + uint32_t des3; +} FTGMAC100Desc; + +/* + * Specific RTL8211E MII Registers + */ +#define RTL8211E_MII_PHYCR 16 /* PHY Specific Control */ +#define RTL8211E_MII_PHYSR 17 /* PHY Specific Status */ +#define RTL8211E_MII_INER 18 /* Interrupt Enable */ +#define RTL8211E_MII_INSR 19 /* Interrupt Status */ +#define RTL8211E_MII_RXERC 24 /* Receive Error Counter */ +#define RTL8211E_MII_LDPSR 27 /* Link Down Power Saving */ +#define RTL8211E_MII_EPAGSR 30 /* Extension Page Select */ +#define RTL8211E_MII_PAGSEL 31 /* Page Select */ + +/* + * RTL8211E Interrupt Status + */ +#define PHY_INT_AUTONEG_ERROR (1 << 15) +#define PHY_INT_PAGE_RECV (1 << 12) +#define PHY_INT_AUTONEG_COMPLETE (1 << 11) +#define PHY_INT_LINK_STATUS (1 << 10) +#define PHY_INT_ERROR (1 << 9) +#define PHY_INT_DOWN (1 << 8) +#define PHY_INT_JABBER (1 << 0) + +/* + * Max frame size for the receiving buffer + */ +#define FTGMAC100_MAX_FRAME_SIZE 10240 + +/* Limits depending on the type of the frame + * + * 9216 for Jumbo frames (+ 4 for VLAN) + * 1518 for other frames (+ 4 for VLAN) + */ +static int ftgmac100_max_frame_size(FTGMAC100State *s) +{ + return (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518) + 4; +} + +static void ftgmac100_update_irq(FTGMAC100State *s) +{ + qemu_set_irq(s->irq, s->isr & s->ier); +} + +/* + * The MII phy could raise a GPIO to the processor which in turn + * could be handled as an interrpt by the OS. + * For now we don't handle any GPIO/interrupt line, so the OS will + * have to poll for the PHY status. + */ +static void phy_update_irq(FTGMAC100State *s) +{ + ftgmac100_update_irq(s); +} + +static void phy_update_link(FTGMAC100State *s) +{ + /* Autonegotiation status mirrors link status. */ + if (qemu_get_queue(s->nic)->link_down) { + s->phy_status &= ~(MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); + s->phy_int |= PHY_INT_DOWN; + } else { + s->phy_status |= (MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); + s->phy_int |= PHY_INT_AUTONEG_COMPLETE; + } + phy_update_irq(s); +} + +static void ftgmac100_set_link(NetClientState *nc) +{ + phy_update_link(FTGMAC100(qemu_get_nic_opaque(nc))); +} + +static void phy_reset(FTGMAC100State *s) +{ + s->phy_status = (MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD | + MII_BMSR_10T_HD | MII_BMSR_EXTSTAT | MII_BMSR_MFPS | + MII_BMSR_AN_COMP | MII_BMSR_AUTONEG | MII_BMSR_LINK_ST | + MII_BMSR_EXTCAP); + s->phy_control = (MII_BMCR_AUTOEN | MII_BMCR_FD | MII_BMCR_SPEED1000); + s->phy_advertise = (MII_ANAR_PAUSE_ASYM | MII_ANAR_PAUSE | MII_ANAR_TXFD | + MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 | + MII_ANAR_CSMACD); + s->phy_int_mask = 0; + s->phy_int = 0; +} + +static uint32_t do_phy_read(FTGMAC100State *s, int reg) +{ + uint32_t val; + + switch (reg) { + case MII_BMCR: /* Basic Control */ + val = s->phy_control; + break; + case MII_BMSR: /* Basic Status */ + val = s->phy_status; + break; + case MII_PHYID1: /* ID1 */ + val = RTL8211E_PHYID1; + break; + case MII_PHYID2: /* ID2 */ + val = RTL8211E_PHYID2; + break; + case MII_ANAR: /* Auto-neg advertisement */ + val = s->phy_advertise; + break; + case MII_ANLPAR: /* Auto-neg Link Partner Ability */ + val = (MII_ANLPAR_ACK | MII_ANLPAR_PAUSE | MII_ANLPAR_TXFD | + MII_ANLPAR_TX | MII_ANLPAR_10FD | MII_ANLPAR_10 | + MII_ANLPAR_CSMACD); + break; + case MII_ANER: /* Auto-neg Expansion */ + val = MII_ANER_NWAY; + break; + case MII_CTRL1000: /* 1000BASE-T control */ + val = (MII_CTRL1000_HALF | MII_CTRL1000_FULL); + break; + case MII_STAT1000: /* 1000BASE-T status */ + val = MII_STAT1000_FULL; + break; + case RTL8211E_MII_INSR: /* Interrupt status. */ + val = s->phy_int; + s->phy_int = 0; + phy_update_irq(s); + break; + case RTL8211E_MII_INER: /* Interrupt enable */ + val = s->phy_int_mask; + break; + case RTL8211E_MII_PHYCR: + case RTL8211E_MII_PHYSR: + case RTL8211E_MII_RXERC: + case RTL8211E_MII_LDPSR: + case RTL8211E_MII_EPAGSR: + case RTL8211E_MII_PAGSEL: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + val = 0; + break; + } + + return val; +} + +#define MII_BMCR_MASK (MII_BMCR_LOOPBACK | MII_BMCR_SPEED100 | \ + MII_BMCR_SPEED | MII_BMCR_AUTOEN | MII_BMCR_PDOWN | \ + MII_BMCR_FD | MII_BMCR_CTST) +#define MII_ANAR_MASK 0x2d7f + +static void do_phy_write(FTGMAC100State *s, int reg, uint32_t val) +{ + switch (reg) { + case MII_BMCR: /* Basic Control */ + if (val & MII_BMCR_RESET) { + phy_reset(s); + } else { + s->phy_control = val & MII_BMCR_MASK; + /* Complete autonegotiation immediately. */ + if (val & MII_BMCR_AUTOEN) { + s->phy_status |= MII_BMSR_AN_COMP; + } + } + break; + case MII_ANAR: /* Auto-neg advertisement */ + s->phy_advertise = (val & MII_ANAR_MASK) | MII_ANAR_TX; + break; + case RTL8211E_MII_INER: /* Interrupt enable */ + s->phy_int_mask = val & 0xff; + phy_update_irq(s); + break; + case RTL8211E_MII_PHYCR: + case RTL8211E_MII_PHYSR: + case RTL8211E_MII_RXERC: + case RTL8211E_MII_LDPSR: + case RTL8211E_MII_EPAGSR: + case RTL8211E_MII_PAGSEL: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + break; + } +} + +static int ftgmac100_read_bd(FTGMAC100Desc *bd, dma_addr_t addr) +{ + if (dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to read descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + bd->des0 = le32_to_cpu(bd->des0); + bd->des1 = le32_to_cpu(bd->des1); + bd->des2 = le32_to_cpu(bd->des2); + bd->des3 = le32_to_cpu(bd->des3); + return 0; +} + +static int ftgmac100_write_bd(FTGMAC100Desc *bd, dma_addr_t addr) +{ + FTGMAC100Desc lebd; + + lebd.des0 = cpu_to_le32(bd->des0); + lebd.des1 = cpu_to_le32(bd->des1); + lebd.des2 = cpu_to_le32(bd->des2); + lebd.des3 = cpu_to_le32(bd->des3); + if (dma_memory_write(&address_space_memory, addr, &lebd, sizeof(lebd))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to write descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + return 0; +} + +static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, + uint32_t tx_descriptor) +{ + int frame_size = 0; + uint8_t *ptr = s->frame; + uint32_t addr = tx_descriptor; + uint32_t flags = 0; + int max_frame_size = ftgmac100_max_frame_size(s); + + while (1) { + FTGMAC100Desc bd; + int len; + + if (ftgmac100_read_bd(&bd, addr) || + ((bd.des0 & FTGMAC100_TXDES0_TXDMA_OWN) == 0)) { + /* Run out of descriptors to transmit. */ + s->isr |= FTGMAC100_INT_NO_NPTXBUF; + break; + } + + /* record transmit flags as they are valid only on the first + * segment */ + if (bd.des0 & FTGMAC100_TXDES0_FTS) { + flags = bd.des1; + } + + len = bd.des0 & 0x3FFF; + if (frame_size + len > max_frame_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n", + __func__, len); + len = max_frame_size - frame_size; + } + + if (dma_memory_read(&address_space_memory, bd.des3, ptr, len)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to read packet @ 0x%x\n", + __func__, bd.des3); + s->isr |= FTGMAC100_INT_NO_NPTXBUF; + break; + } + + ptr += len; + frame_size += len; + if (bd.des0 & FTGMAC100_TXDES0_LTS) { + if (flags & FTGMAC100_TXDES1_IP_CHKSUM) { + net_checksum_calculate(s->frame, frame_size); + } + /* Last buffer in frame. */ + qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size); + ptr = s->frame; + frame_size = 0; + if (flags & FTGMAC100_TXDES1_TXIC) { + s->isr |= FTGMAC100_INT_XPKT_ETH; + } + } + + if (flags & FTGMAC100_TXDES1_TX2FIC) { + s->isr |= FTGMAC100_INT_XPKT_FIFO; + } + bd.des0 &= ~FTGMAC100_TXDES0_TXDMA_OWN; + + /* Write back the modified descriptor. */ + ftgmac100_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if (bd.des0 & s->txdes0_edotr) { + addr = tx_ring; + } else { + addr += sizeof(FTGMAC100Desc); + } + } + + s->tx_descriptor = addr; + + ftgmac100_update_irq(s); +} + +static int ftgmac100_can_receive(NetClientState *nc) +{ + FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); + FTGMAC100Desc bd; + + if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) + != (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) { + return 0; + } + + if (ftgmac100_read_bd(&bd, s->rx_descriptor)) { + return 0; + } + return !(bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY); +} + +/* + * This is purely informative. The HW can poll the RW (and RX) ring + * buffers for available descriptors but we don't need to trigger a + * timer for that in qemu. + */ +static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) +{ + /* Polling times : + * + * Speed TIME_SEL=0 TIME_SEL=1 + * + * 10 51.2 ms 819.2 ms + * 100 5.12 ms 81.92 ms + * 1000 1.024 ms 16.384 ms + */ + static const int div[] = { 20, 200, 1000 }; + + uint32_t cnt = 1024 * FTGMAC100_APTC_RXPOLL_CNT(s->aptcr); + uint32_t speed = (s->maccr & FTGMAC100_MACCR_FAST_MODE) ? 1 : 0; + uint32_t period; + + if (s->aptcr & FTGMAC100_APTC_RXPOLL_TIME_SEL) { + cnt <<= 4; + } + + if (s->maccr & FTGMAC100_MACCR_GIGA_MODE) { + speed = 2; + } + + period = cnt / div[speed]; + + return period; +} + +static void ftgmac100_reset(DeviceState *d) +{ + FTGMAC100State *s = FTGMAC100(d); + + /* Reset the FTGMAC100 */ + s->isr = 0; + s->ier = 0; + s->rx_enabled = 0; + s->rx_ring = 0; + s->rbsr = 0x640; + s->rx_descriptor = 0; + s->tx_ring = 0; + s->tx_descriptor = 0; + s->math[0] = 0; + s->math[1] = 0; + s->itc = 0; + s->aptcr = 1; + s->dblac = 0x00022f00; + s->revr = 0; + s->fear1 = 0; + s->tpafcr = 0xf1; + + s->maccr = 0; + s->phycr = 0; + s->phydata = 0; + s->fcr = 0x400; + + /* and the PHY */ + phy_reset(s); +} + +static uint64_t ftgmac100_read(void *opaque, hwaddr addr, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + + switch (addr & 0xff) { + case FTGMAC100_ISR: + return s->isr; + case FTGMAC100_IER: + return s->ier; + case FTGMAC100_MAC_MADR: + return (s->conf.macaddr.a[0] << 8) | s->conf.macaddr.a[1]; + case FTGMAC100_MAC_LADR: + return ((uint32_t) s->conf.macaddr.a[2] << 24) | + (s->conf.macaddr.a[3] << 16) | (s->conf.macaddr.a[4] << 8) | + s->conf.macaddr.a[5]; + case FTGMAC100_MATH0: + return s->math[0]; + case FTGMAC100_MATH1: + return s->math[1]; + case FTGMAC100_ITC: + return s->itc; + case FTGMAC100_DBLAC: + return s->dblac; + case FTGMAC100_REVR: + return s->revr; + case FTGMAC100_FEAR1: + return s->fear1; + case FTGMAC100_TPAFCR: + return s->tpafcr; + case FTGMAC100_FCR: + return s->fcr; + case FTGMAC100_MACCR: + return s->maccr; + case FTGMAC100_PHYCR: + return s->phycr; + case FTGMAC100_PHYDATA: + return s->phydata; + + /* We might want to support these one day */ + case FTGMAC100_HPTXPD: /* High Priority Transmit Poll Demand */ + case FTGMAC100_HPTXR_BADR: /* High Priority Transmit Ring Base Address */ + case FTGMAC100_MACSR: /* MAC Status Register (MACSR) */ + qemu_log_mask(LOG_UNIMP, "%s: read to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + return 0; + } +} + +static void ftgmac100_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + int reg; + + switch (addr & 0xff) { + case FTGMAC100_ISR: /* Interrupt status */ + s->isr &= ~value; + break; + case FTGMAC100_IER: /* Interrupt control */ + s->ier = value; + break; + case FTGMAC100_MAC_MADR: /* MAC */ + s->conf.macaddr.a[0] = value >> 8; + s->conf.macaddr.a[1] = value; + break; + case FTGMAC100_MAC_LADR: + s->conf.macaddr.a[2] = value >> 24; + s->conf.macaddr.a[3] = value >> 16; + s->conf.macaddr.a[4] = value >> 8; + s->conf.macaddr.a[5] = value; + break; + case FTGMAC100_MATH0: /* Multicast Address Hash Table 0 */ + s->math[0] = value; + break; + case FTGMAC100_MATH1: /* Multicast Address Hash Table 1 */ + s->math[1] = value; + break; + case FTGMAC100_ITC: /* TODO: Interrupt Timer Control */ + s->itc = value; + break; + case FTGMAC100_RXR_BADR: /* Ring buffer address */ + s->rx_ring = value; + s->rx_descriptor = s->rx_ring; + break; + + case FTGMAC100_RBSR: /* DMA buffer size */ + s->rbsr = value; + break; + + case FTGMAC100_NPTXR_BADR: /* Transmit buffer address */ + s->tx_ring = value; + s->tx_descriptor = s->tx_ring; + break; + + case FTGMAC100_NPTXPD: /* Trigger transmit */ + if ((s->maccr & (FTGMAC100_MACCR_TXDMA_EN | FTGMAC100_MACCR_TXMAC_EN)) + == (FTGMAC100_MACCR_TXDMA_EN | FTGMAC100_MACCR_TXMAC_EN)) { + /* TODO: high priority tx ring */ + ftgmac100_do_tx(s, s->tx_ring, s->tx_descriptor); + } + if (ftgmac100_can_receive(qemu_get_queue(s->nic))) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + break; + + case FTGMAC100_RXPD: /* Receive Poll Demand Register */ + if (ftgmac100_can_receive(qemu_get_queue(s->nic))) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + break; + + case FTGMAC100_APTC: /* Automatic polling */ + s->aptcr = value; + + if (FTGMAC100_APTC_RXPOLL_CNT(s->aptcr)) { + ftgmac100_rxpoll(s); + } + + if (FTGMAC100_APTC_TXPOLL_CNT(s->aptcr)) { + qemu_log_mask(LOG_UNIMP, "%s: no transmit polling\n", __func__); + } + break; + + case FTGMAC100_MACCR: /* MAC Device control */ + s->maccr = value; + if (value & FTGMAC100_MACCR_SW_RST) { + ftgmac100_reset(DEVICE(s)); + } + + if (ftgmac100_can_receive(qemu_get_queue(s->nic))) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + break; + + case FTGMAC100_PHYCR: /* PHY Device control */ + reg = FTGMAC100_PHYCR_REG(value); + s->phycr = value; + if (value & FTGMAC100_PHYCR_MIIWR) { + do_phy_write(s, reg, s->phydata & 0xffff); + s->phycr &= ~FTGMAC100_PHYCR_MIIWR; + } else { + s->phydata = do_phy_read(s, reg) << 16; + s->phycr &= ~FTGMAC100_PHYCR_MIIRD; + } + break; + case FTGMAC100_PHYDATA: + s->phydata = value & 0xffff; + break; + case FTGMAC100_DBLAC: /* DMA Burst Length and Arbitration Control */ + s->dblac = value; + break; + case FTGMAC100_REVR: /* Feature Register */ + /* TODO: Only Old MDIO interface is supported */ + s->revr = value & ~FTGMAC100_REVR_NEW_MDIO_INTERFACE; + break; + case FTGMAC100_FEAR1: /* Feature Register 1 */ + s->fear1 = value; + break; + case FTGMAC100_TPAFCR: /* Transmit Priority Arbitration and FIFO Control */ + s->tpafcr = value; + break; + case FTGMAC100_FCR: /* Flow Control */ + s->fcr = value; + break; + + case FTGMAC100_HPTXPD: /* High Priority Transmit Poll Demand */ + case FTGMAC100_HPTXR_BADR: /* High Priority Transmit Ring Base Address */ + case FTGMAC100_MACSR: /* MAC Status Register (MACSR) */ + qemu_log_mask(LOG_UNIMP, "%s: write to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + + ftgmac100_update_irq(s); +} + +static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len) +{ + unsigned mcast_idx; + + if (s->maccr & FTGMAC100_MACCR_RX_ALL) { + return 1; + } + + switch (get_eth_packet_type(PKT_GET_ETH_HDR(buf))) { + case ETH_PKT_BCAST: + if (!(s->maccr & FTGMAC100_MACCR_RX_BROADPKT)) { + return 0; + } + break; + case ETH_PKT_MCAST: + if (!(s->maccr & FTGMAC100_MACCR_RX_MULTIPKT)) { + if (!(s->maccr & FTGMAC100_MACCR_HT_MULTI_EN)) { + return 0; + } + + /* TODO: this does not seem to work for ftgmac100 */ + mcast_idx = compute_mcast_idx(buf); + if (!(s->math[mcast_idx / 32] & (1 << (mcast_idx % 32)))) { + return 0; + } + } + break; + case ETH_PKT_UCAST: + if (memcmp(s->conf.macaddr.a, buf, 6)) { + return 0; + } + break; + } + + return 1; +} + +static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, + size_t len) +{ + FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); + FTGMAC100Desc bd; + uint32_t flags = 0; + uint32_t addr; + uint32_t crc; + uint32_t buf_addr; + uint8_t *crc_ptr; + uint32_t buf_len; + size_t size = len; + uint32_t first = FTGMAC100_RXDES0_FRS; + int max_frame_size = ftgmac100_max_frame_size(s); + + if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) + != (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) { + return -1; + } + + /* TODO : Pad to minimum Ethernet frame length */ + /* handle small packets. */ + if (size < 10) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped frame of %zd bytes\n", + __func__, size); + return size; + } + + if (size < 64 && !(s->maccr & FTGMAC100_MACCR_RX_RUNT)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped runt frame of %zd bytes\n", + __func__, size); + return size; + } + + if (!ftgmac100_filter(s, buf, size)) { + return size; + } + + /* 4 bytes for the CRC. */ + size += 4; + crc = cpu_to_be32(crc32(~0, buf, size)); + crc_ptr = (uint8_t *) &crc; + + /* Huge frames are truncated. */ + if (size > max_frame_size) { + size = max_frame_size; + qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %zd bytes\n", + __func__, size); + flags |= FTGMAC100_RXDES0_FTL; + } + + switch (get_eth_packet_type(PKT_GET_ETH_HDR(buf))) { + case ETH_PKT_BCAST: + flags |= FTGMAC100_RXDES0_BROADCAST; + break; + case ETH_PKT_MCAST: + flags |= FTGMAC100_RXDES0_MULTICAST; + break; + case ETH_PKT_UCAST: + break; + } + + addr = s->rx_descriptor; + while (size > 0) { + if (!ftgmac100_can_receive(nc)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unexpected packet\n", __func__); + return -1; + } + + if (ftgmac100_read_bd(&bd, addr) || + (bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY)) { + /* No descriptors available. Bail out. */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Lost end of frame\n", + __func__); + s->isr |= FTGMAC100_INT_NO_RXBUF; + break; + } + buf_len = (size <= s->rbsr) ? size : s->rbsr; + bd.des0 |= buf_len & 0x3fff; + size -= buf_len; + + /* The last 4 bytes are the CRC. */ + if (size < 4) { + buf_len += size - 4; + } + buf_addr = bd.des3; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + buf += buf_len; + if (size < 4) { + dma_memory_write(&address_space_memory, buf_addr + buf_len, + crc_ptr, 4 - size); + crc_ptr += 4 - size; + } + + bd.des0 |= first | FTGMAC100_RXDES0_RXPKT_RDY; + first = 0; + if (size == 0) { + /* Last buffer in frame. */ + bd.des0 |= flags | FTGMAC100_RXDES0_LRS; + s->isr |= FTGMAC100_INT_RPKT_BUF; + } else { + s->isr |= FTGMAC100_INT_RPKT_FIFO; + } + ftgmac100_write_bd(&bd, addr); + if (bd.des0 & s->rxdes0_edorr) { + addr = s->rx_ring; + } else { + addr += sizeof(FTGMAC100Desc); + } + } + s->rx_descriptor = addr; + + ftgmac100_update_irq(s); + return len; +} + +static const MemoryRegionOps ftgmac100_ops = { + .read = ftgmac100_read, + .write = ftgmac100_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ftgmac100_cleanup(NetClientState *nc) +{ + FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); + + s->nic = NULL; +} + +static NetClientInfo net_ftgmac100_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = ftgmac100_can_receive, + .receive = ftgmac100_receive, + .cleanup = ftgmac100_cleanup, + .link_status_changed = ftgmac100_set_link, +}; + +static void ftgmac100_realize(DeviceState *dev, Error **errp) +{ + FTGMAC100State *s = FTGMAC100(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + if (s->aspeed) { + s->txdes0_edotr = FTGMAC100_TXDES0_EDOTR_ASPEED; + s->rxdes0_edorr = FTGMAC100_RXDES0_EDORR_ASPEED; + } else { + s->txdes0_edotr = FTGMAC100_TXDES0_EDOTR; + s->rxdes0_edorr = FTGMAC100_RXDES0_EDORR; + } + + memory_region_init_io(&s->iomem, OBJECT(dev), &ftgmac100_ops, s, + TYPE_FTGMAC100, 0x2000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->conf.peers.ncs[0] = nd_table[0].netdev; + + s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf, + object_get_typename(OBJECT(dev)), DEVICE(dev)->id, + s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); + + s->frame = g_malloc(FTGMAC100_MAX_FRAME_SIZE); +} + +static const VMStateDescription vmstate_ftgmac100 = { + .name = TYPE_FTGMAC100, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_state, FTGMAC100State), + VMSTATE_UINT32(isr, FTGMAC100State), + VMSTATE_UINT32(ier, FTGMAC100State), + VMSTATE_UINT32(rx_enabled, FTGMAC100State), + VMSTATE_UINT32(rx_ring, FTGMAC100State), + VMSTATE_UINT32(rbsr, FTGMAC100State), + VMSTATE_UINT32(tx_ring, FTGMAC100State), + VMSTATE_UINT32(rx_descriptor, FTGMAC100State), + VMSTATE_UINT32(tx_descriptor, FTGMAC100State), + VMSTATE_UINT32_ARRAY(math, FTGMAC100State, 2), + VMSTATE_UINT32(itc, FTGMAC100State), + VMSTATE_UINT32(aptcr, FTGMAC100State), + VMSTATE_UINT32(dblac, FTGMAC100State), + VMSTATE_UINT32(revr, FTGMAC100State), + VMSTATE_UINT32(fear1, FTGMAC100State), + VMSTATE_UINT32(tpafcr, FTGMAC100State), + VMSTATE_UINT32(maccr, FTGMAC100State), + VMSTATE_UINT32(phycr, FTGMAC100State), + VMSTATE_UINT32(phydata, FTGMAC100State), + VMSTATE_UINT32(fcr, FTGMAC100State), + VMSTATE_UINT32(phy_status, FTGMAC100State), + VMSTATE_UINT32(phy_control, FTGMAC100State), + VMSTATE_UINT32(phy_advertise, FTGMAC100State), + VMSTATE_UINT32(phy_int, FTGMAC100State), + VMSTATE_UINT32(phy_int_mask, FTGMAC100State), + VMSTATE_UINT32(txdes0_edotr, FTGMAC100State), + VMSTATE_UINT32(rxdes0_edorr, FTGMAC100State), + VMSTATE_END_OF_LIST() + } +}; + +static Property ftgmac100_properties[] = { + DEFINE_PROP_BOOL("aspeed", FTGMAC100State, aspeed, false), + DEFINE_NIC_PROPERTIES(FTGMAC100State, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ftgmac100_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_ftgmac100; + dc->reset = ftgmac100_reset; + dc->props = ftgmac100_properties; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->realize = ftgmac100_realize; + dc->desc = "Faraday FTGMAC100 Gigabit Ethernet emulation"; +} + +static const TypeInfo ftgmac100_info = { + .name = TYPE_FTGMAC100, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FTGMAC100State), + .class_init = ftgmac100_class_init, +}; + +static void ftgmac100_register_types(void) +{ + type_register_static(&ftgmac100_info); +} + +type_init(ftgmac100_register_types) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index c32168077a..7d091c9259 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -510,6 +510,10 @@ static int peer_attach(VirtIONet *n, int index) return 0; } + if (n->max_queues == 1) { + return 0; + } + return tap_enable(nc->peer); } diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 467cbb9cb8..27fde46126 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -380,20 +380,8 @@ static void pci_vpb_reset(DeviceState *d) static void pci_vpb_init(Object *obj) { - PCIHostState *h = PCI_HOST_BRIDGE(obj); PCIVPBState *s = PCI_VPB(obj); - memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32); - memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32); - - pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), "pci", - &s->pci_mem_space, &s->pci_io_space, - PCI_DEVFN(11, 0), TYPE_PCI_BUS); - h->bus = &s->pci_bus; - - object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST); - qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus)); - /* Window sizes for VersatilePB; realview_pci's init will override */ s->mem_win_size[0] = 0x0c000000; s->mem_win_size[1] = 0x10000000; @@ -403,10 +391,22 @@ static void pci_vpb_init(Object *obj) static void pci_vpb_realize(DeviceState *dev, Error **errp) { PCIVPBState *s = PCI_VPB(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); pci_map_irq_fn mapfn; int i; + memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32); + memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32); + + pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), dev, "pci", + &s->pci_mem_space, &s->pci_io_space, + PCI_DEVFN(11, 0), TYPE_PCI_BUS); + h->bus = &s->pci_bus; + + object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST); + qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus)); + for (i = 0; i < 4; i++) { sysbus_init_irq(sbd, &s->irq[i]); } @@ -503,8 +503,6 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data) dc->reset = pci_vpb_reset; dc->vmsd = &pci_vpb_vmstate; dc->props = pci_vpb_properties; - /* Reason: object_unref() hangs */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo pci_vpb_info = { @@ -526,19 +524,10 @@ static void pci_realview_init(Object *obj) s->mem_win_size[2] = 0x08000000; } -static void pci_realview_class_init(ObjectClass *class, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(class); - - /* Reason: object_unref() hangs */ - dc->cannot_destroy_with_object_finalize_yet = true; -} - static const TypeInfo pci_realview_info = { .name = "realview_pci", .parent = TYPE_VERSATILE_PCI, .instance_init = pci_realview_init, - .class_init = pci_realview_class_init, }; static void versatile_pci_register_types(void) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e6b08e1988..259483b1c0 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -869,6 +869,10 @@ static void do_pci_unregister_device(PCIDevice *pci_dev) pci_dev->bus->devices[pci_dev->devfn] = NULL; pci_config_free(pci_dev); + if (memory_region_is_mapped(&pci_dev->bus_master_enable_region)) { + memory_region_del_subregion(&pci_dev->bus_master_container_region, + &pci_dev->bus_master_enable_region); + } address_space_destroy(&pci_dev->bus_master_as); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 6ee566d658..35db949dbc 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1524,16 +1524,16 @@ static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, /* Consume invalid HPTEs */ while ((index < htabslots) && !HPTE_VALID(HPTE(spapr->htab, index))) { - index++; CLEAN_HPTE(HPTE(spapr->htab, index)); + index++; } /* Consume valid HPTEs */ chunkstart = index; while ((index < htabslots) && (index - chunkstart < USHRT_MAX) && HPTE_VALID(HPTE(spapr->htab, index))) { - index++; CLEAN_HPTE(HPTE(spapr->htab, index)); + index++; } if (index > chunkstart) { @@ -2790,6 +2790,12 @@ static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } + if (cc->nr_threads != smp_threads) { + error_setg(errp, "invalid nr-threads %d, must be %d", + cc->nr_threads, smp_threads); + return; + } + core_slot = spapr_find_cpu_slot(MACHINE(hotplug_dev), cc->core_id, &index); if (!core_slot) { error_setg(&local_err, "core id %d out of range", cc->core_id); @@ -3096,6 +3102,11 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) xic->ics_resend = spapr_ics_resend; xic->icp_get = spapr_icp_get; ispc->print_info = spapr_pic_print_info; + /* Force NUMA node memory size to be a multiple of + * SPAPR_MEMORY_BLOCK_SIZE (256M) since that's the granularity + * in which LMBs are represented and hot-added + */ + mc->numa_mem_align_shift = 28; } static const TypeInfo spapr_machine_info = { @@ -3180,6 +3191,7 @@ static void spapr_machine_2_8_class_options(MachineClass *mc) { spapr_machine_2_9_class_options(mc); SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_8); + mc->numa_mem_align_shift = 23; } DEFINE_SPAPR_MACHINE(2_8, "2.8", false); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 150f6bf2c7..a1cdc875b1 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -135,6 +135,17 @@ static uint32_t set_allocation_state(sPAPRDRConnector *drc, if (!drc->dev) { return RTAS_OUT_NO_SUCH_INDICATOR; } + if (drc->awaiting_release && drc->awaiting_allocation) { + /* kernel is acknowledging a previous hotplug event + * while we are already removing it. + * it's safe to ignore awaiting_allocation here since we know the + * situation is predicated on the guest either already having done + * so (boot-time hotplug), or never being able to acquire in the + * first place (hotplug followed by immediate unplug). + */ + drc->awaiting_allocation_skippable = true; + return RTAS_OUT_NO_SUCH_INDICATOR; + } } if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) { @@ -436,9 +447,11 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d, } if (drc->awaiting_allocation) { - drc->awaiting_release = true; - trace_spapr_drc_awaiting_allocation(get_index(drc)); - return; + if (!drc->awaiting_allocation_skippable) { + drc->awaiting_release = true; + trace_spapr_drc_awaiting_allocation(get_index(drc)); + return; + } } drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE; @@ -448,6 +461,7 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d, } drc->awaiting_release = false; + drc->awaiting_allocation_skippable = false; g_free(drc->fdt); drc->fdt = NULL; drc->fdt_start_offset = 0; diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 28ea20440e..fb8d640a7e 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -11,11 +11,51 @@ #include "qemu/osdep.h" #include "ccw-device.h" +static void ccw_device_refill_ids(CcwDevice *dev) +{ + SubchDev *sch = dev->sch; + + assert(sch); + + dev->dev_id.cssid = sch->cssid; + dev->dev_id.ssid = sch->ssid; + dev->dev_id.devid = sch->devno; + dev->dev_id.valid = true; + + dev->subch_id.cssid = sch->cssid; + dev->subch_id.ssid = sch->ssid; + dev->subch_id.devid = sch->schid; + dev->subch_id.valid = true; +} + +static void ccw_device_realize(CcwDevice *dev, Error **errp) +{ + ccw_device_refill_ids(dev); +} + +static Property ccw_device_properties[] = { + DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), + DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), + DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ccw_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + CCWDeviceClass *k = CCW_DEVICE_CLASS(klass); + + k->realize = ccw_device_realize; + k->refill_ids = ccw_device_refill_ids; + dc->props = ccw_device_properties; +} + static const TypeInfo ccw_device_info = { .name = TYPE_CCW_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(CcwDevice), .class_size = sizeof(CCWDeviceClass), + .class_init = ccw_device_class_init, .abstract = true, }; diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 59ba01b6c5..89c8e5dff7 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -19,12 +19,19 @@ typedef struct CcwDevice { DeviceState parent_obj; SubchDev *sch; /* <cssid>.<ssid>.<device number> */ - CssDevId bus_id; + /* The user-set busid of the virtual ccw device. */ + CssDevId devno; + /* The actual busid of the virtual ccw device. */ + CssDevId dev_id; + /* The actual busid of the virtual subchannel. */ + CssDevId subch_id; } CcwDevice; typedef struct CCWDeviceClass { DeviceClass parent_class; void (*unplug)(HotplugHandler *, DeviceState *, Error **); + void (*realize)(CcwDevice *, Error **); + void (*refill_ids)(CcwDevice *); } CCWDeviceClass; static inline CcwDevice *to_ccw_dev_fast(DeviceState *d) diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 9a7f7ee60c..b54ac01d37 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -107,6 +107,9 @@ VirtualCssBus *virtual_css_bus_init(void) /* Enable hotplugging */ qbus_set_hotplug_handler(bus, dev, &error_abort); + css_register_io_adapters(CSS_IO_ADAPTER_VIRTIO, true, false, + &error_abort); + return cbus; } diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 37caa98195..c03bb20bc9 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -47,7 +47,6 @@ typedef struct IoAdapter { uint32_t id; uint8_t type; uint8_t isc; - QTAILQ_ENTRY(IoAdapter) sibling; } IoAdapter; typedef struct ChannelSubSys { @@ -61,7 +60,7 @@ typedef struct ChannelSubSys { uint64_t chnmon_area; CssImage *css[MAX_CSSID + 1]; uint8_t default_cssid; - QTAILQ_HEAD(, IoAdapter) io_adapters; + IoAdapter *io_adapters[CSS_IO_ADAPTER_TYPE_NUMS][MAX_ISC + 1]; QTAILQ_HEAD(, IndAddr) indicator_addresses; } ChannelSubSys; @@ -72,7 +71,6 @@ static ChannelSubSys channel_subsys = { .do_crw_mchk = true, .crws_lost = false, .chnmon_active = false, - .io_adapters = QTAILQ_HEAD_INITIALIZER(channel_subsys.io_adapters), .indicator_addresses = QTAILQ_HEAD_INITIALIZER(channel_subsys.indicator_addresses), }; @@ -155,44 +153,67 @@ int css_create_css_image(uint8_t cssid, bool default_image) return 0; } -int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, - bool maskable, uint32_t *id) +uint32_t css_get_adapter_id(CssIoAdapterType type, uint8_t isc) { + if (type >= CSS_IO_ADAPTER_TYPE_NUMS || isc > MAX_ISC || + !channel_subsys.io_adapters[type][isc]) { + return -1; + } + + return channel_subsys.io_adapters[type][isc]->id; +} + +/** + * css_register_io_adapters: Register I/O adapters per ISC during init + * + * @swap: an indication if byte swap is needed. + * @maskable: an indication if the adapter is subject to the mask operation. + * @errp: location to store error information. + */ +void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, + Error **errp) +{ + uint32_t id; + int ret, isc; IoAdapter *adapter; - bool found = false; - int ret; S390FLICState *fs = s390_get_flic(); S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); - *id = 0; - QTAILQ_FOREACH(adapter, &channel_subsys.io_adapters, sibling) { - if ((adapter->type == type) && (adapter->isc == isc)) { - *id = adapter->id; - found = true; - ret = 0; + /* + * Disallow multiple registrations for the same device type. + * Report an error if registering for an already registered type. + */ + if (channel_subsys.io_adapters[type][0]) { + error_setg(errp, "Adapters for type %d already registered", type); + } + + for (isc = 0; isc <= MAX_ISC; isc++) { + id = (type << 3) | isc; + ret = fsc->register_io_adapter(fs, id, isc, swap, maskable); + if (ret == 0) { + adapter = g_new0(IoAdapter, 1); + adapter->id = id; + adapter->isc = isc; + adapter->type = type; + channel_subsys.io_adapters[type][isc] = adapter; + } else { + error_setg_errno(errp, -ret, "Unexpected error %d when " + "registering adapter %d", ret, id); break; } - if (adapter->id >= *id) { - *id = adapter->id + 1; - } - } - if (found) { - goto out; } - adapter = g_new0(IoAdapter, 1); - ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable); - if (ret == 0) { - adapter->id = *id; - adapter->isc = isc; - adapter->type = type; - QTAILQ_INSERT_TAIL(&channel_subsys.io_adapters, adapter, sibling); - } else { - g_free(adapter); - fprintf(stderr, "Unexpected error %d when registering adapter %d\n", - ret, *id); + + /* + * No need to free registered adapters in kvm: kvm will clean up + * when the machine goes away. + */ + if (ret) { + for (isc--; isc >= 0; isc--) { + g_free(channel_subsys.io_adapters[type][isc]); + channel_subsys.io_adapters[type][isc] = NULL; + } } -out: - return ret; + } static void css_clear_io_interrupt(uint16_t subchannel_id, @@ -1894,6 +1915,13 @@ PropertyInfo css_devid_propinfo = { .set = set_css_devid, }; +PropertyInfo css_devid_ro_propinfo = { + .name = "str", + .description = "Read-only identifier of an I/O device in the channel " + "subsystem, example: fe.1.23ab", + .get = get_css_devid, +}; + SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp) { uint16_t schid = 0; diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 69b0291e8a..a8a1bab50a 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -23,15 +23,17 @@ #include "hw/pci/msi.h" #include "qemu/error-report.h" -/* #define DEBUG_S390PCI_BUS */ -#ifdef DEBUG_S390PCI_BUS -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) +#ifndef DEBUG_S390PCI_BUS +#define DEBUG_S390PCI_BUS 0 #endif +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_S390PCI_BUS) { \ + fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + S390pciState *s390_get_phb(void) { static S390pciState *phb; @@ -579,6 +581,9 @@ static int s390_pcihost_init(SysBusDevice *dev) s->bus_no = 0; QTAILQ_INIT(&s->pending_sei); QTAILQ_INIT(&s->zpci_devs); + + css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false, &error_abort); + return 0; } diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index dcbf4820c9..cf142a3e68 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -30,7 +30,6 @@ #define FH_MASK_INDEX 0x0000ffff #define FH_SHM_VFIO 0x00010000 #define FH_SHM_EMUL 0x00020000 -#define S390_PCIPT_ADAPTER 2 #define ZPCI_MAX_FID 0xffffffff #define ZPCI_MAX_UID 0xffff #define UID_UNDEFINED 0 diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index d2a8c0a083..314a9cbad4 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -20,15 +20,17 @@ #include "qemu/error-report.h" #include "sysemu/hw_accel.h" -/* #define DEBUG_S390PCI_INST */ -#ifdef DEBUG_S390PCI_INST -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) +#ifndef DEBUG_S390PCI_INST +#define DEBUG_S390PCI_INST 0 #endif +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_S390PCI_INST) { \ + fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + static void s390_set_status_code(CPUS390XState *env, uint8_t r, uint64_t status_code) { @@ -731,12 +733,10 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) { int ret, len; + uint8_t isc = FIB_DATA_ISC(ldl_p(&fib.data)); - ret = css_register_io_adapter(S390_PCIPT_ADAPTER, - FIB_DATA_ISC(ldl_p(&fib.data)), true, false, - &pbdev->routes.adapter.adapter_id); - assert(ret == 0); - + pbdev->routes.adapter.adapter_id = css_get_adapter_id( + CSS_IO_ADAPTER_PCI, isc); pbdev->summary_ind = get_indicator(ldq_p(&fib.aisb), sizeof(uint64_t)); len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long); pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len); @@ -755,7 +755,7 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data)); pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv); pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data)); - pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data)); + pbdev->isc = isc; pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data)); pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data)); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 40914fde6f..04bd0ebe40 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -113,12 +113,13 @@ static void ccw_init(MachineState *machine) s390_sclp_init(); s390_memory_init(machine->ram_size); + s390_flic_init(); + /* get a BUS */ css_bus = virtual_css_bus_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, "s390-ccw.img", "s390-netboot.img", true); - s390_flic_init(); dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE); object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE, @@ -336,6 +337,9 @@ static const TypeInfo ccw_machine_info = { } \ type_init(ccw_machine_register_##suffix) +#define CCW_COMPAT_2_9 \ + HW_COMPAT_2_9 + #define CCW_COMPAT_2_8 \ HW_COMPAT_2_8 \ {\ @@ -402,14 +406,26 @@ static const TypeInfo ccw_machine_info = { .value = "0",\ }, +static void ccw_machine_2_10_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_2_10_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(2_10, "2.10", true); + static void ccw_machine_2_9_instance_options(MachineState *machine) { + ccw_machine_2_10_instance_options(machine); } static void ccw_machine_2_9_class_options(MachineClass *mc) { + ccw_machine_2_10_class_options(mc); + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_9); } -DEFINE_CCW_MACHINE(2_9, "2.9", true); +DEFINE_CCW_MACHINE(2_9, "2.9", false); static void ccw_machine_2_8_instance_options(MachineState *machine) { diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 00b3bde4e9..e7167e3d05 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -616,10 +616,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) dev->routes.adapter.ind_offset = ind_bit; dev->routes.adapter.summary_offset = 7; cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); - ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, - dev->thinint_isc, true, false, - &dev->routes.adapter.adapter_id); - assert(ret == 0); + dev->routes.adapter.adapter_id = css_get_adapter_id( + CSS_IO_ADAPTER_VIRTIO, + dev->thinint_isc); sch->thinint_active = ((dev->indicators != NULL) && (dev->summary_indicator != NULL)); sch->curr_status.scsw.count = ccw.count - len; @@ -680,7 +679,8 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); CcwDevice *ccw_dev = CCW_DEVICE(dev); - SubchDev *sch = css_create_virtual_sch(ccw_dev->bus_id, errp); + CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); + SubchDev *sch = css_create_virtual_sch(ccw_dev->devno, errp); Error *err = NULL; if (!sch) { @@ -689,8 +689,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) if (!virtio_ccw_rev_max(dev) && dev->force_revision_1) { error_setg(&err, "Invalid value of property max_rev " "(is %d expected >= 1)", virtio_ccw_rev_max(dev)); - error_propagate(errp, err); - return; + goto out_err; } sch->driver_data = dev; @@ -705,7 +704,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) trace_virtio_ccw_new_device( sch->cssid, sch->ssid, sch->schid, sch->devno, - ccw_dev->bus_id.valid ? "user-configured" : "auto-configured"); + ccw_dev->devno.valid ? "user-configured" : "auto-configured"); if (!kvm_eventfds_enabled()) { dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; @@ -713,13 +712,23 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) if (k->realize) { k->realize(dev, &err); + if (err) { + goto out_err; + } } + + ck->realize(ccw_dev, &err); if (err) { - error_propagate(errp, err); - css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); - ccw_dev->sch = NULL; - g_free(sch); + goto out_err; } + + return; + +out_err: + error_propagate(errp, err); + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + ccw_dev->sch = NULL; + g_free(sch); } static int virtio_ccw_exit(VirtioCcwDevice *dev) @@ -1261,12 +1270,17 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); CcwDevice *ccw_dev = CCW_DEVICE(d); + CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); int len; s->driver_data = dev; subch_device_load(s, f); + /* Re-fill subch_id after loading the subchannel states.*/ + if (ck->refill_ids) { + ck->refill_ids(ccw_dev); + } len = qemu_get_be32(f); if (len != 0) { dev->indicators = get_indicator(qemu_get_be64(f), len); @@ -1293,9 +1307,9 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) dev->thinint_isc = qemu_get_byte(f); dev->revision = qemu_get_be32(f); if (s->thinint_active) { - return css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, - dev->thinint_isc, true, false, - &dev->routes.adapter.adapter_id); + dev->routes.adapter.adapter_id = css_get_adapter_id( + CSS_IO_ADAPTER_VIRTIO, + dev->thinint_isc); } return 0; @@ -1354,7 +1368,6 @@ static void virtio_ccw_device_unplugged(DeviceState *d) /**************** Virtio-ccw Bus Device Descriptions *******************/ static Property virtio_ccw_net_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1383,7 +1396,6 @@ static const TypeInfo virtio_ccw_net = { }; static Property virtio_ccw_blk_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1412,7 +1424,6 @@ static const TypeInfo virtio_ccw_blk = { }; static Property virtio_ccw_serial_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1441,7 +1452,6 @@ static const TypeInfo virtio_ccw_serial = { }; static Property virtio_ccw_balloon_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1470,7 +1480,6 @@ static const TypeInfo virtio_ccw_balloon = { }; static Property virtio_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1500,7 +1509,6 @@ static const TypeInfo virtio_ccw_scsi = { #ifdef CONFIG_VHOST_SCSI static Property vhost_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), @@ -1538,7 +1546,6 @@ static void virtio_ccw_rng_instance_init(Object *obj) } static Property virtio_ccw_rng_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1567,7 +1574,6 @@ static const TypeInfo virtio_ccw_rng = { }; static Property virtio_ccw_crypto_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1694,7 +1700,6 @@ static const TypeInfo virtio_ccw_bus_info = { #ifdef CONFIG_VIRTFS static Property virtio_ccw_9p_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1743,7 +1748,6 @@ static const TypeInfo virtio_ccw_9p_info = { #ifdef CONFIG_VHOST_VSOCK static Property vhost_vsock_ccw_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), @@ -1757,9 +1761,7 @@ static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + error_propagate(errp, err); } static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index e3d59b7c83..84b8caf901 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -291,7 +291,7 @@ static int megasas_map_sgl(MegasasState *s, MegasasCmd *cmd, union mfi_sgl *sgl) if (cmd->iov_size > iov_size) { trace_megasas_iovec_overflow(cmd->index, iov_size, cmd->iov_size); } else if (cmd->iov_size < iov_size) { - trace_megasas_iovec_underflow(cmd->iov_size, iov_size, cmd->iov_size); + trace_megasas_iovec_underflow(cmd->index, iov_size, cmd->iov_size); } cmd->iov_offset = 0; return 0; @@ -1924,8 +1924,8 @@ static int megasas_handle_abort(MegasasState *s, MegasasCmd *cmd) abort_ctx &= (uint64_t)0xFFFFFFFF; } if (abort_cmd->context != abort_ctx) { - trace_megasas_abort_invalid_context(cmd->index, abort_cmd->index, - abort_cmd->context); + trace_megasas_abort_invalid_context(cmd->index, abort_cmd->context, + abort_cmd->index); s->event_count++; return MFI_STAT_ABORT_NOT_POSSIBLE; } diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 2933119e7d..a55ff87c22 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -237,9 +237,8 @@ static void scsi_read_complete(void * opaque, int ret) assert(max_transfer); stl_be_p(&r->buf[8], max_transfer); /* Also take care of the opt xfer len. */ - if (ldl_be_p(&r->buf[12]) > max_transfer) { - stl_be_p(&r->buf[12], max_transfer); - } + stl_be_p(&r->buf[12], + MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); } scsi_req_data(&r->req, len); scsi_req_unref(&r->req); diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index c491ece1f2..f53bc179da 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -233,9 +233,11 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) } } - virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, + virtio_scsi_common_realize(dev, vhost_dummy_handle_output, - vhost_dummy_handle_output); + vhost_dummy_handle_output, + vhost_dummy_handle_output, + &err); if (err != NULL) { error_propagate(errp, err); goto close_fd; diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 74c95e0e60..944ea4eb53 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -52,28 +52,40 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) static bool virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) { - VirtIOSCSI *s = (VirtIOSCSI *)vdev; + bool progress; + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + virtio_scsi_acquire(s); assert(s->ctx && s->dataplane_started); - return virtio_scsi_handle_cmd_vq(s, vq); + progress = virtio_scsi_handle_cmd_vq(s, vq); + virtio_scsi_release(s); + return progress; } static bool virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { + bool progress; VirtIOSCSI *s = VIRTIO_SCSI(vdev); + virtio_scsi_acquire(s); assert(s->ctx && s->dataplane_started); - return virtio_scsi_handle_ctrl_vq(s, vq); + progress = virtio_scsi_handle_ctrl_vq(s, vq); + virtio_scsi_release(s); + return progress; } static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, VirtQueue *vq) { + bool progress; VirtIOSCSI *s = VIRTIO_SCSI(vdev); + virtio_scsi_acquire(s); assert(s->ctx && s->dataplane_started); - return virtio_scsi_handle_event_vq(s, vq); + progress = virtio_scsi_handle_event_vq(s, vq); + virtio_scsi_release(s); + return progress; } static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 1dbc4bced9..46a3e3f280 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -422,31 +422,15 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) } } -static inline void virtio_scsi_acquire(VirtIOSCSI *s) -{ - if (s->ctx) { - aio_context_acquire(s->ctx); - } -} - -static inline void virtio_scsi_release(VirtIOSCSI *s) -{ - if (s->ctx) { - aio_context_release(s->ctx); - } -} - bool virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) { VirtIOSCSIReq *req; bool progress = false; - virtio_scsi_acquire(s); while ((req = virtio_scsi_pop_req(s, vq))) { progress = true; virtio_scsi_handle_ctrl_req(s, req); } - virtio_scsi_release(s); return progress; } @@ -460,7 +444,9 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) return; } } + virtio_scsi_acquire(s); virtio_scsi_handle_ctrl_vq(s, vq); + virtio_scsi_release(s); } static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) @@ -604,7 +590,6 @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - virtio_scsi_acquire(s); do { virtio_queue_set_notification(vq, 0); @@ -632,7 +617,6 @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { virtio_scsi_handle_cmd_req_submit(s, req); } - virtio_scsi_release(s); return progress; } @@ -647,7 +631,9 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) return; } } + virtio_scsi_acquire(s); virtio_scsi_handle_cmd_vq(s, vq); + virtio_scsi_release(s); } static void virtio_scsi_get_config(VirtIODevice *vdev, @@ -723,12 +709,10 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, return; } - virtio_scsi_acquire(s); - req = virtio_scsi_pop_req(s, vs->event_vq); if (!req) { s->events_dropped = true; - goto out; + return; } if (s->events_dropped) { @@ -738,7 +722,7 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, if (virtio_scsi_parse_req(req, 0, sizeof(VirtIOSCSIEvent))) { virtio_scsi_bad_req(req); - goto out; + return; } evt = &req->resp.event; @@ -758,19 +742,14 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, evt->lun[3] = dev->lun & 0xFF; } virtio_scsi_complete_req(req); -out: - virtio_scsi_release(s); } bool virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) { - virtio_scsi_acquire(s); if (s->events_dropped) { virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); - virtio_scsi_release(s); return true; } - virtio_scsi_release(s); return false; } @@ -784,7 +763,9 @@ static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) return; } } + virtio_scsi_acquire(s); virtio_scsi_handle_event_vq(s, vq); + virtio_scsi_release(s); } static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) @@ -794,8 +775,10 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) && dev->type != TYPE_ROM) { + virtio_scsi_acquire(s); virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, sense.asc | (sense.ascq << 8)); + virtio_scsi_release(s); } } @@ -817,9 +800,11 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, } if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + virtio_scsi_acquire(s); virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); + virtio_scsi_release(s); } } @@ -831,9 +816,11 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, SCSIDevice *sd = SCSI_DEVICE(dev); if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + virtio_scsi_acquire(s); virtio_scsi_push_event(s, sd, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED); + virtio_scsi_release(s); } qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); @@ -854,10 +841,11 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .load_request = virtio_scsi_load_request, }; -void virtio_scsi_common_realize(DeviceState *dev, Error **errp, +void virtio_scsi_common_realize(DeviceState *dev, VirtIOHandleOutput ctrl, VirtIOHandleOutput evt, - VirtIOHandleOutput cmd) + VirtIOHandleOutput cmd, + Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev); @@ -891,9 +879,11 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) VirtIOSCSI *s = VIRTIO_SCSI(dev); Error *err = NULL; - virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl, + virtio_scsi_common_realize(dev, + virtio_scsi_handle_ctrl, virtio_scsi_handle_event, - virtio_scsi_handle_cmd); + virtio_scsi_handle_cmd, + &err); if (err != NULL) { error_propagate(errp, err); return; diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 6d06968f8b..8f520cec1c 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -277,8 +277,15 @@ static void r2d_init(MachineState *machine) sysbus_connect_irq(busdev, 2, irq[PCI_INTC]); sysbus_connect_irq(busdev, 3, irq[PCI_INTD]); - sm501_init(address_space_mem, 0x10000000, SM501_VRAM_SIZE, - irq[SM501], serial_hds[2]); + dev = qdev_create(NULL, "sysbus-sm501"); + busdev = SYS_BUS_DEVICE(dev); + qdev_prop_set_uint32(dev, "vram-size", SM501_VRAM_SIZE); + qdev_prop_set_uint32(dev, "base", 0x10000000); + qdev_prop_set_ptr(dev, "chr-state", serial_hds[2]); + qdev_init_nofail(dev); + sysbus_mmio_map(busdev, 0, 0x10000000); + sysbus_mmio_map(busdev, 1, 0x13e00000); + sysbus_connect_irq(busdev, 0, irq[SM501]); /* onboard CF (True IDE mode, Master only). */ dinfo = drive_get(IF_IDE, 0, 0); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 873cd7df9a..5f022cc08d 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -491,7 +491,6 @@ static void tcx_init(hwaddr addr, qemu_irq irq, int vram_size, int width, qdev_prop_set_uint16(dev, "width", width); qdev_prop_set_uint16(dev, "height", height); qdev_prop_set_uint16(dev, "depth", depth); - qdev_prop_set_uint64(dev, "prom_addr", addr); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); @@ -544,7 +543,6 @@ static void cg3_init(hwaddr addr, qemu_irq irq, int vram_size, int width, qdev_prop_set_uint16(dev, "width", width); qdev_prop_set_uint16(dev, "height", height); qdev_prop_set_uint16(dev, "depth", depth); - qdev_prop_set_uint64(dev, "prom-addr", addr); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index 0c189348ae..a2ec3920f8 100644 --- a/hw/timer/exynos4210_mct.c +++ b/hw/timer/exynos4210_mct.c @@ -53,6 +53,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu/main-loop.h" @@ -1372,8 +1373,9 @@ break; case L0_TCNTO: case L1_TCNTO: case L0_ICNTO: case L1_ICNTO: case L0_FRCNTO: case L1_FRCNTO: - fprintf(stderr, "\n[exynos4210.mct: write to RO register " - TARGET_FMT_plx "]\n\n", offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.mct: write to RO register " TARGET_FMT_plx, + offset); break; case L0_INT_CSTAT: case L1_INT_CSTAT: diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c index f5765075c7..87f63f057e 100644 --- a/hw/timer/exynos4210_pwm.c +++ b/hw/timer/exynos4210_pwm.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" @@ -252,9 +253,9 @@ static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.pwm: bad read offset " TARGET_FMT_plx, + offset); break; } return value; @@ -343,9 +344,9 @@ static void exynos4210_pwm_write(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.pwm: bad write offset " TARGET_FMT_plx, + offset); break; } diff --git a/hw/timer/exynos4210_rtc.c b/hw/timer/exynos4210_rtc.c index 1a648c5d9e..4607833e3e 100644 --- a/hw/timer/exynos4210_rtc.c +++ b/hw/timer/exynos4210_rtc.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" @@ -370,9 +371,9 @@ static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.rtc: bad read offset " TARGET_FMT_plx, + offset); break; } return value; @@ -433,9 +434,9 @@ static void exynos4210_rtc_write(void *opaque, hwaddr offset, if (value > TICNT_THRESHOLD) { s->reg_ticcnt = value; } else { - fprintf(stderr, - "[exynos4210.rtc: bad TICNT value %u ]\n", - (uint32_t)value); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.rtc: bad TICNT value %u", + (uint32_t)value); } break; @@ -500,9 +501,9 @@ static void exynos4210_rtc_write(void *opaque, hwaddr offset, break; default: - fprintf(stderr, - "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n", - offset); + qemu_log_mask(LOG_GUEST_ERROR, + "exynos4210.rtc: bad write offset " TARGET_FMT_plx, + offset); break; } diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 24f1608b4b..5939b273b9 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -762,9 +762,7 @@ static void usb_set_attached(Object *obj, bool value, Error **errp) if (value) { usb_device_attach(dev, &err); - if (err) { - error_propagate(errp, err); - } + error_propagate(errp, err); } else { usb_device_detach(dev); } diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index f0af852709..a2d3143bf4 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2063,7 +2063,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) { XHCIState *xhci = epctx->xhci; - XHCIStreamContext *stctx; + XHCIStreamContext *stctx = NULL; XHCITransfer *xfer; XHCIRing *ring; USBEndpoint *ep = NULL; @@ -2186,6 +2186,8 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) break; } } + /* update ring dequeue ptr */ + xhci_set_ep_state(xhci, epctx, stctx, epctx->state); epctx->kick_active--; ep = xhci_epid_to_usbep(epctx); diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index c9876a5b0f..f9c8eafe06 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -159,7 +159,10 @@ static void usb_host_attach_kernel(USBHostDevice *s); #define BULK_TIMEOUT 0 /* unlimited */ #define INTR_TIMEOUT 0 /* unlimited */ -#if LIBUSBX_API_VERSION >= 0x01000103 +#ifndef LIBUSB_API_VERSION +# define LIBUSB_API_VERSION LIBUSBX_API_VERSION +#endif +#if LIBUSB_API_VERSION >= 0x01000103 # define HAVE_STREAMS 1 #endif @@ -269,7 +272,7 @@ static int usb_host_get_port(libusb_device *dev, char *port, size_t len) size_t off; int rc, i; -#if LIBUSBX_API_VERSION >= 0x01000102 +#if LIBUSB_API_VERSION >= 0x01000102 rc = libusb_get_port_numbers(dev, path, 7); #else rc = libusb_get_port_path(ctx, dev, path, 7); diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f3ba9b9007..6b33b9f55d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -478,8 +478,13 @@ static void vfio_listener_region_add(MemoryListener *listener, giommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; giommu->container = container; - giommu->n.notify = vfio_iommu_map_notify; - giommu->n.notifier_flags = IOMMU_NOTIFIER_ALL; + llend = int128_add(int128_make64(section->offset_within_region), + section->size); + llend = int128_sub(llend, int128_one()); + iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, + IOMMU_NOTIFIER_ALL, + section->offset_within_region, + int128_get64(llend)); QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); @@ -550,7 +555,8 @@ static void vfio_listener_region_del(MemoryListener *listener, VFIOGuestIOMMU *giommu; QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { - if (giommu->iommu == section->mr) { + if (giommu->iommu == section->mr && + giommu->n.start == section->offset_within_region) { memory_region_unregister_iommu_notifier(giommu->iommu, &giommu->n); QLIST_REMOVE(giommu, giommu_next); diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index e995e32dee..349085ea12 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -660,7 +660,7 @@ static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr) VFIOConfigWindowQuirk *window; if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) || - !vdev->vga || nr != 5) { + !vdev->vga || nr != 5 || !vdev->bars[5].ioport) { return; } @@ -1367,45 +1367,14 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) uint16_t cmd_orig, cmd; Error *err = NULL; - /* This must be an Intel VGA device. */ - if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || nr != 4) { - return; - } - - /* - * IGD is not a standard, they like to change their specs often. We - * only attempt to support back to SandBridge and we hope that newer - * devices maintain compatibility with generation 8. - */ - gen = igd_gen(vdev); - if (gen != 6 && gen != 8) { - error_report("IGD device %s is unsupported by IGD quirks, " - "try SandyBridge or newer", vdev->vbasedev.name); - return; - } - - /* - * Regardless of running in UPT or legacy mode, the guest graphics - * driver may attempt to use stolen memory, however only legacy mode - * has BIOS support for reserving stolen memory in the guest VM. - * Emulate the GMCH register in all cases and zero out the stolen - * memory size here. Legacy mode may request allocation and re-write - * this below. - */ - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); - gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8)); - - /* GMCH is read-only, emulated */ - pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); - pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); - pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); - /* - * This must be at address 00:02.0 for us to even onsider enabling - * legacy mode. The vBIOS has dependencies on the PCI bus address. + * This must be an Intel VGA device at address 00:02.0 for us to even + * consider enabling legacy mode. The vBIOS has dependencies on the + * PCI bus address. */ - if (&vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || nr != 4 || + &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), 0, PCI_DEVFN(0x2, 0))) { return; } @@ -1425,6 +1394,18 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } /* + * IGD is not a standard, they like to change their specs often. We + * only attempt to support back to SandBridge and we hope that newer + * devices maintain compatibility with generation 8. + */ + gen = igd_gen(vdev); + if (gen != 6 && gen != 8) { + error_report("IGD device %s is unsupported in legacy mode, " + "try SandyBridge or newer", vdev->vbasedev.name); + return; + } + + /* * Most of what we're doing here is to enable the ROM to run, so if * there's no ROM, there's no point in setting up this quirk. * NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support. @@ -1479,6 +1460,8 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) goto out; } + gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); + /* * If IGD VGA Disable is clear (expected) and VGA is not already enabled, * try to enable it. Probably shouldn't be using legacy mode without VGA, @@ -1549,11 +1532,12 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * when IVD (IGD VGA Disable) is clear, but the claim is that it's unused, * so let's not waste VM memory for it. */ + gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8)); + if (vdev->igd_gms) { if (vdev->igd_gms <= 0x10) { gms_mb = vdev->igd_gms * 32; gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8); - pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); } else { error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms); vdev->igd_gms = 0; @@ -1573,6 +1557,11 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", bdsm_size, sizeof(*bdsm_size)); + /* GMCH is read-only, emulated */ + pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); + pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); + pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); + /* BDSM is read-write, emulated. The BIOS needs to be able to write it */ pci_set_long(vdev->pdev.config + IGD_BDSM, 0); pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 6926eedd3f..1f7a7c1ae1 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -11,8 +11,11 @@ virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u" # hw/virtio/virtio-rng.c virtio_rng_guest_not_ready(void *rng) "rng %p: guest not ready" +virtio_rng_cpu_is_stopped(void *rng, int size) "rng %p: cpu is stopped, dropping %d bytes" +virtio_rng_popped(void *rng) "rng %p: elem popped" virtio_rng_pushed(void *rng, size_t len) "rng %p: %zd bytes pushed" virtio_rng_request(void *rng, size_t size, unsigned quota) "rng %p: %zd bytes requested, %u bytes quota left" +virtio_rng_vm_state_change(void *rng, int running, int state) "rng %p: state change to running %d state %d" # hw/virtio/virtio-balloon.c # diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index febe519bbd..0001e60b77 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -425,10 +425,8 @@ static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) static int vhost_dev_has_iommu(struct vhost_dev *dev) { VirtIODevice *vdev = dev->vdev; - AddressSpace *dma_as = vdev->dma_as; - return memory_region_is_iommu(dma_as->root) && - virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); + return virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); } static void *vhost_memory_map(struct vhost_dev *dev, hwaddr addr, @@ -720,6 +718,70 @@ static void vhost_region_del(MemoryListener *listener, } } +static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) +{ + struct vhost_iommu *iommu = container_of(n, struct vhost_iommu, n); + struct vhost_dev *hdev = iommu->hdev; + hwaddr iova = iotlb->iova + iommu->iommu_offset; + + if (hdev->vhost_ops->vhost_invalidate_device_iotlb(hdev, iova, + iotlb->addr_mask + 1)) { + error_report("Fail to invalidate device iotlb"); + } +} + +static void vhost_iommu_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + struct vhost_dev *dev = container_of(listener, struct vhost_dev, + iommu_listener); + struct vhost_iommu *iommu; + Int128 end; + + if (!memory_region_is_iommu(section->mr)) { + return; + } + + iommu = g_malloc0(sizeof(*iommu)); + end = int128_add(int128_make64(section->offset_within_region), + section->size); + end = int128_sub(end, int128_one()); + iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify, + IOMMU_NOTIFIER_UNMAP, + section->offset_within_region, + int128_get64(end)); + iommu->mr = section->mr; + iommu->iommu_offset = section->offset_within_address_space - + section->offset_within_region; + iommu->hdev = dev; + memory_region_register_iommu_notifier(section->mr, &iommu->n); + QLIST_INSERT_HEAD(&dev->iommu_list, iommu, iommu_next); + /* TODO: can replay help performance here? */ +} + +static void vhost_iommu_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + struct vhost_dev *dev = container_of(listener, struct vhost_dev, + iommu_listener); + struct vhost_iommu *iommu; + + if (!memory_region_is_iommu(section->mr)) { + return; + } + + QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) { + if (iommu->mr == section->mr && + iommu->n.start == section->offset_within_region) { + memory_region_unregister_iommu_notifier(iommu->mr, + &iommu->n); + QLIST_REMOVE(iommu, iommu_next); + g_free(iommu); + break; + } + } +} + static void vhost_region_nop(MemoryListener *listener, MemoryRegionSection *section) { @@ -1161,17 +1223,6 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) event_notifier_cleanup(&vq->masked_notifier); } -static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) -{ - struct vhost_dev *hdev = container_of(n, struct vhost_dev, n); - - if (hdev->vhost_ops->vhost_invalidate_device_iotlb(hdev, - iotlb->iova, - iotlb->addr_mask + 1)) { - error_report("Fail to invalidate device iotlb"); - } -} - int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout) { @@ -1244,8 +1295,10 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .priority = 10 }; - hdev->n.notify = vhost_iommu_unmap_notify; - hdev->n.notifier_flags = IOMMU_NOTIFIER_UNMAP; + hdev->iommu_listener = (MemoryListener) { + .region_add = vhost_iommu_region_add, + .region_del = vhost_iommu_region_del, + }; if (hdev->migration_blocker == NULL) { if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) { @@ -1455,8 +1508,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } if (vhost_dev_has_iommu(hdev)) { - memory_region_register_iommu_notifier(vdev->dma_as->root, - &hdev->n); + memory_listener_register(&hdev->iommu_listener, vdev->dma_as); } r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem); @@ -1538,8 +1590,7 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) if (vhost_dev_has_iommu(hdev)) { hdev->vhost_ops->vhost_set_iotlb_callback(hdev, false); - memory_region_unregister_iommu_notifier(vdev->dma_as->root, - &hdev->n); + memory_listener_unregister(&hdev->iommu_listener); } vhost_log_put(hdev, true); hdev->started = false; diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index a886011e75..3042232daf 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/error-report.h" +#include "qapi/error.h" #include "hw/qdev.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio.h" @@ -48,20 +49,33 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); + Error *local_err = NULL; DPRINTF("%s: plug device.\n", qbus->name); if (klass->pre_plugged != NULL) { - klass->pre_plugged(qbus->parent, errp); + klass->pre_plugged(qbus->parent, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } } /* Get the features of the plugged device. */ assert(vdc->get_features != NULL); vdev->host_features = vdc->get_features(vdev, vdev->host_features, - errp); + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } if (klass->device_plugged != NULL) { - klass->device_plugged(qbus->parent, errp); + klass->device_plugged(qbus->parent, &local_err); + } + if (local_err) { + error_propagate(errp, local_err); + return; } if (klass->get_dma_as != NULL && has_iommu) { diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 9639f4e89b..a6ee501051 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -53,6 +53,15 @@ static void chr_read(void *opaque, const void *buf, size_t size) return; } + /* we can't modify the virtqueue until + * our state is fully synced + */ + + if (!runstate_check(RUN_STATE_RUNNING)) { + trace_virtio_rng_cpu_is_stopped(vrng, size); + return; + } + vrng->quota_remaining -= size; offset = 0; @@ -61,6 +70,7 @@ static void chr_read(void *opaque, const void *buf, size_t size) if (!elem) { break; } + trace_virtio_rng_popped(vrng); len = iov_from_buf(elem->in_sg, elem->in_num, 0, buf + offset, size - offset); offset += len; @@ -120,17 +130,21 @@ static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) return f; } -static int virtio_rng_post_load(void *opaque, int version_id) +static void virtio_rng_vm_state_change(void *opaque, int running, + RunState state) { VirtIORNG *vrng = opaque; + trace_virtio_rng_vm_state_change(vrng, running, state); + /* We may have an element ready but couldn't process it due to a quota - * limit. Make sure to try again after live migration when the quota may - * have been reset. + * limit or because CPU was stopped. Make sure to try again when the + * CPU restart. */ - virtio_rng_process(vrng); - return 0; + if (running && is_guest_ready(vrng)) { + virtio_rng_process(vrng); + } } static void check_rate_limit(void *opaque) @@ -198,6 +212,9 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, check_rate_limit, vrng); vrng->activate_timer = true; + + vrng->vmstate = qemu_add_vm_change_state_handler(virtio_rng_vm_state_change, + vrng); } static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) @@ -205,6 +222,7 @@ static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIORNG *vrng = VIRTIO_RNG(dev); + qemu_del_vm_change_state_handler(vrng->vmstate); timer_del(vrng->rate_limit_timer); timer_free(vrng->rate_limit_timer); virtio_cleanup(vdev); @@ -218,7 +236,6 @@ static const VMStateDescription vmstate_virtio_rng = { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, - .post_load = virtio_rng_post_load, }; static Property virtio_rng_properties[] = { diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 82b6060b2a..03592c542a 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1528,7 +1528,18 @@ static void virtio_queue_notify_vq(VirtQueue *vq) void virtio_queue_notify(VirtIODevice *vdev, int n) { - virtio_queue_notify_vq(&vdev->vq[n]); + VirtQueue *vq = &vdev->vq[n]; + + if (unlikely(!vq->vring.desc || vdev->broken)) { + return; + } + + trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); + if (vq->handle_aio_output) { + event_notifier_set(&vq->host_notifier); + } else if (vq->handle_output) { + vq->handle_output(vdev, vq); + } } uint16_t virtio_queue_vector(VirtIODevice *vdev, int n) |