diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/arm/boot.c | 64 | ||||
-rw-r--r-- | hw/arm/exynos4_boards.c | 7 | ||||
-rw-r--r-- | hw/arm/pxa2xx.c | 14 | ||||
-rw-r--r-- | hw/arm/stellaris.c | 60 | ||||
-rw-r--r-- | hw/arm/xlnx-zynqmp.c | 6 | ||||
-rw-r--r-- | hw/char/exynos4210_uart.c | 8 | ||||
-rw-r--r-- | hw/core/null-machine.c | 6 | ||||
-rw-r--r-- | hw/core/qdev-properties-system.c | 2 | ||||
-rw-r--r-- | hw/core/qdev-properties.c | 3 | ||||
-rw-r--r-- | hw/core/qdev.c | 9 | ||||
-rw-r--r-- | hw/i386/intel_iommu.c | 442 | ||||
-rw-r--r-- | hw/i386/intel_iommu_internal.h | 1 | ||||
-rw-r--r-- | hw/i386/trace-events | 10 | ||||
-rw-r--r-- | hw/misc/exynos4210_pmu.c | 4 | ||||
-rw-r--r-- | hw/net/cadence_gem.c | 45 | ||||
-rw-r--r-- | hw/timer/exynos4210_mct.c | 6 | ||||
-rw-r--r-- | hw/timer/exynos4210_pwm.c | 13 | ||||
-rw-r--r-- | hw/timer/exynos4210_rtc.c | 19 | ||||
-rw-r--r-- | hw/vfio/common.c | 12 | ||||
-rw-r--r-- | hw/virtio/vhost.c | 13 |
20 files changed, 626 insertions, 118 deletions
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/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/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 4f671588b0..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); 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/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 e885e650fb..79c2014135 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -409,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..695d7c4216 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -1037,13 +1037,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 +1056,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/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/trace-events b/hw/i386/trace-events index baed874a80..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 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/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/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/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/virtio/vhost.c b/hw/virtio/vhost.c index 613494dcc2..0001e60b77 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -736,14 +736,20 @@ static void vhost_iommu_region_add(MemoryListener *listener, 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)); - iommu->n.notify = vhost_iommu_unmap_notify; - iommu->n.notifier_flags = IOMMU_NOTIFIER_UNMAP; + 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; @@ -765,7 +771,8 @@ static void vhost_iommu_region_del(MemoryListener *listener, } QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) { - if (iommu->mr == section->mr) { + 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); |