diff options
46 files changed, 1426 insertions, 353 deletions
@@ -1474,7 +1474,7 @@ fi gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits" gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags" -gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" +gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags" gcc_flags="-Wno-initializer-overrides $gcc_flags" gcc_flags="-Wno-string-plus-int $gcc_flags" @@ -190,6 +190,7 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, s.cpu = cpu; s.info.read_memory_func = target_read_memory; + s.info.read_memory_inner_func = NULL; s.info.buffer_vma = code; s.info.buffer_length = size; s.info.print_address_func = generic_print_address; @@ -2115,6 +2115,7 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) return; } vaddr = (cpu->mem_io_vaddr & TARGET_PAGE_MASK) + offset; + vaddr = cc->adjust_watchpoint_address(cpu, vaddr, len); QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { if (cpu_watchpoint_address_matches(wp, vaddr, len) && (wp->flags & flags)) { diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index b3e7f07b61..571e4f097b 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -31,6 +31,7 @@ #define ASPEED_SOC_SCU_BASE 0x1E6E2000 #define ASPEED_SOC_SRAM_BASE 0x1E720000 #define ASPEED_SOC_TIMER_BASE 0x1E782000 +#define ASPEED_SOC_WDT_BASE 0x1E785000 #define ASPEED_SOC_I2C_BASE 0x1E78A000 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; @@ -170,6 +171,10 @@ static void aspeed_soc_init(Object *obj) sc->info->silicon_rev); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size", &error_abort); + + 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()); } static void aspeed_soc_realize(DeviceState *dev, Error **errp) @@ -286,6 +291,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, ASPEED_SOC_SDMC_BASE); + + /* Watch dog */ + object_property_set_bool(OBJECT(&s->wdt), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE); } static void aspeed_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 039812a3fd..5610ffc9ce 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -53,6 +53,26 @@ static uint8_t integrator_spd[128] = { 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40 }; +static const VMStateDescription vmstate_integratorcm = { + .name = "integratorcm", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cm_osc, IntegratorCMState), + VMSTATE_UINT32(cm_ctrl, IntegratorCMState), + VMSTATE_UINT32(cm_lock, IntegratorCMState), + VMSTATE_UINT32(cm_auxosc, IntegratorCMState), + VMSTATE_UINT32(cm_sdram, IntegratorCMState), + VMSTATE_UINT32(cm_init, IntegratorCMState), + VMSTATE_UINT32(cm_flags, IntegratorCMState), + VMSTATE_UINT32(cm_nvflags, IntegratorCMState), + VMSTATE_UINT32(int_level, IntegratorCMState), + VMSTATE_UINT32(irq_enabled, IntegratorCMState), + VMSTATE_UINT32(fiq_enabled, IntegratorCMState), + VMSTATE_END_OF_LIST() + } +}; + static uint64_t integratorcm_read(void *opaque, hwaddr offset, unsigned size) { @@ -309,6 +329,18 @@ typedef struct icp_pic_state { qemu_irq parent_fiq; } icp_pic_state; +static const VMStateDescription vmstate_icp_pic = { + .name = "icp_pic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, icp_pic_state), + VMSTATE_UINT32(irq_enabled, icp_pic_state), + VMSTATE_UINT32(fiq_enabled, icp_pic_state), + VMSTATE_END_OF_LIST() + } +}; + static void icp_pic_update(icp_pic_state *s) { uint32_t flags; @@ -438,6 +470,16 @@ typedef struct ICPCtrlRegsState { #define ICP_INTREG_WPROT (1 << 0) #define ICP_INTREG_CARDIN (1 << 3) +static const VMStateDescription vmstate_icp_control = { + .name = "icp_control", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(intreg_state, ICPCtrlRegsState), + VMSTATE_END_OF_LIST() + } +}; + static uint64_t icp_control_read(void *opaque, hwaddr offset, unsigned size) { @@ -535,27 +577,42 @@ static void integratorcp_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + char **cpustr; ObjectClass *cpu_oc; + CPUClass *cc; Object *cpuobj; ARMCPU *cpu; + const char *typename; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1); qemu_irq pic[32]; DeviceState *dev, *sic, *icp; int i; + Error *err = NULL; if (!cpu_model) { cpu_model = "arm926"; } - cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + cpustr = g_strsplit(cpu_model, ",", 2); + + cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); if (!cpu_oc) { fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } + typename = object_class_get_name(cpu_oc); - cpuobj = object_new(object_class_get_name(cpu_oc)); + cc = CPU_CLASS(cpu_oc); + cc->parse_features(typename, cpustr[1], &err); + g_strfreev(cpustr); + if (err) { + error_report_err(err); + exit(1); + } + + cpuobj = object_new(typename); /* By default ARM1176 CPUs have EL3 enabled. This board does not * currently support EL3 so the CPU EL3 property is disabled before @@ -640,6 +697,21 @@ static void core_class_init(ObjectClass *klass, void *data) dc->props = core_properties; dc->realize = integratorcm_realize; + dc->vmsd = &vmstate_integratorcm; +} + +static void icp_pic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_icp_pic; +} + +static void icp_control_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_icp_control; } static const TypeInfo core_info = { @@ -655,6 +727,7 @@ static const TypeInfo icp_pic_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pic_state), .instance_init = icp_pic_init, + .class_init = icp_pic_class_init, }; static const TypeInfo icp_ctrl_regs_info = { @@ -662,6 +735,7 @@ static const TypeInfo icp_ctrl_regs_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ICPCtrlRegsState), .instance_init = icp_control_init, + .class_init = icp_control_class_init, }; static void integratorcp_register_types(void) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 794a3ada71..9edcd49740 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -21,6 +21,7 @@ #include "exec/address-spaces.h" #include "sysemu/sysemu.h" #include "hw/char/pl011.h" +#include "hw/misc/unimp.h" #define GPIO_A 0 #define GPIO_B 1 @@ -1220,6 +1221,40 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, 0x40024000, 0x40025000, 0x40026000}; static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; + /* Memory map of SoC devices, from + * Stellaris LM3S6965 Microcontroller Data Sheet (rev I) + * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf + * + * 40000000 wdtimer (unimplemented) + * 40002000 i2c (unimplemented) + * 40004000 GPIO + * 40005000 GPIO + * 40006000 GPIO + * 40007000 GPIO + * 40008000 SSI + * 4000c000 UART + * 4000d000 UART + * 4000e000 UART + * 40020000 i2c + * 40021000 i2c (unimplemented) + * 40024000 GPIO + * 40025000 GPIO + * 40026000 GPIO + * 40028000 PWM (unimplemented) + * 4002c000 QEI (unimplemented) + * 4002d000 QEI (unimplemented) + * 40030000 gptimer + * 40031000 gptimer + * 40032000 gptimer + * 40033000 gptimer + * 40038000 ADC + * 4003c000 analogue comparator (unimplemented) + * 40048000 ethernet + * 400fc000 hibernation module (unimplemented) + * 400fd000 flash memory control (unimplemented) + * 400fe000 system control + */ + DeviceState *gpio_dev[7], *nvic; qemu_irq gpio_in[7][8]; qemu_irq gpio_out[7][8]; @@ -1370,6 +1405,19 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, } } } + + /* Add dummy regions for the devices we don't implement yet, + * so guest accesses don't cause unlogged crashes. + */ + create_unimplemented_device("wdtimer", 0x40000000, 0x1000); + create_unimplemented_device("i2c-0", 0x40002000, 0x1000); + create_unimplemented_device("i2c-2", 0x40021000, 0x1000); + create_unimplemented_device("PWM", 0x40028000, 0x1000); + create_unimplemented_device("QEI-0", 0x4002c000, 0x1000); + create_unimplemented_device("QEI-1", 0x4002d000, 0x1000); + create_unimplemented_device("analogue-comparator", 0x4003c000, 0x1000); + create_unimplemented_device("hibernation", 0x400fc000, 0x1000); + create_unimplemented_device("flash-control", 0x400fd000, 0x1000); } /* FIXME: Figure out how to generate these from stellaris_boards. */ diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 57834423b9..06ba02e2a3 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -933,9 +933,12 @@ static void device_set_realized(Object *obj, bool value, Error **errp) } if (qdev_get_vmsd(dev)) { - vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, - dev->instance_id_alias, - dev->alias_required_for_version); + if (vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, + dev->instance_id_alias, + dev->alias_required_for_version, + &local_err) < 0) { + goto post_realize_fail; + } } QLIST_FOREACH(bus, &dev->child_bus, sibling) { diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 17df24c9d0..6ce8ef744a 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -329,7 +329,7 @@ static void apic_common_realize(DeviceState *dev, Error **errp) instance_id = -1; } vmstate_register_with_alias_id(NULL, instance_id, &vmstate_apic_common, - s, -1, 0); + s, -1, 0, NULL); } static void apic_common_unrealize(DeviceState *dev, Error **errp) diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 1a89615a62..898e4ccfb1 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -6,6 +6,8 @@ common-obj-$(CONFIG_SGA) += sga.o common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o +common-obj-y += unimp.o + obj-$(CONFIG_VMPORT) += vmport.o # ARM devices diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c new file mode 100644 index 0000000000..bcbb585888 --- /dev/null +++ b/hw/misc/unimp.c @@ -0,0 +1,107 @@ +/* "Unimplemented" device + * + * This is a dummy device which accepts and logs all accesses. + * It's useful for stubbing out regions of an SoC or board + * map which correspond to devices that have not yet been + * implemented. This is often sufficient to placate initial + * guest device driver probing such that the system will + * come up. + * + * Copyright Linaro Limited, 2017 + * Written by Peter Maydell + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/unimp.h" +#include "qemu/log.h" +#include "qapi/error.h" + +#define UNIMPLEMENTED_DEVICE(obj) \ + OBJECT_CHECK(UnimplementedDeviceState, (obj), TYPE_UNIMPLEMENTED_DEVICE) + +typedef struct { + SysBusDevice parent_obj; + MemoryRegion iomem; + char *name; + uint64_t size; +} UnimplementedDeviceState; + +static uint64_t unimp_read(void *opaque, hwaddr offset, unsigned size) +{ + UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(opaque); + + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + s->name, size, offset); + return 0; +} + +static void unimp_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(opaque); + + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + s->name, size, value, offset); +} + +static const MemoryRegionOps unimp_ops = { + .read = unimp_read, + .write = unimp_write, + .impl.min_access_size = 1, + .impl.max_access_size = 8, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void unimp_realize(DeviceState *dev, Error **errp) +{ + UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(dev); + + if (s->size == 0) { + error_setg(errp, "property 'size' not specified or zero"); + return; + } + + if (s->name == NULL) { + error_setg(errp, "property 'name' not specified"); + return; + } + + memory_region_init_io(&s->iomem, OBJECT(s), &unimp_ops, s, + s->name, s->size); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static Property unimp_properties[] = { + DEFINE_PROP_UINT64("size", UnimplementedDeviceState, size, 0), + DEFINE_PROP_STRING("name", UnimplementedDeviceState, name), + DEFINE_PROP_END_OF_LIST(), +}; + +static void unimp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = unimp_realize; + dc->props = unimp_properties; +} + +static const TypeInfo unimp_info = { + .name = TYPE_UNIMPLEMENTED_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UnimplementedDeviceState), + .class_init = unimp_class_init, +}; + +static void unimp_register_types(void) +{ + type_register_static(&unimp_info); +} + +type_init(unimp_register_types) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 01fbf228be..5bd5ab6319 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -536,7 +536,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) boundary_count -= block_size - begin; } dma_memory_read(&address_space_memory, s->sdmasysad, - &s->fifo_buffer[begin], s->data_count); + &s->fifo_buffer[begin], s->data_count - begin); s->sdmasysad += s->data_count - begin; if (s->data_count == block_size) { for (n = 0; n < block_size; n++) { diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs index 72e3ffd93c..9589bed63a 100644 --- a/hw/watchdog/Makefile.objs +++ b/hw/watchdog/Makefile.objs @@ -2,3 +2,4 @@ common-obj-y += watchdog.o common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o +common-obj-$(CONFIG_ASPEED_SOC) += wdt_aspeed.o diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c new file mode 100644 index 0000000000..8bbe579b6b --- /dev/null +++ b/hw/watchdog/wdt_aspeed.c @@ -0,0 +1,225 @@ +/* + * ASPEED Watchdog Controller + * + * Copyright (C) 2016-2017 IBM Corp. + * + * 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 "qemu/log.h" +#include "sysemu/watchdog.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/watchdog/wdt_aspeed.h" + +#define WDT_STATUS (0x00 / 4) +#define WDT_RELOAD_VALUE (0x04 / 4) +#define WDT_RESTART (0x08 / 4) +#define WDT_CTRL (0x0C / 4) +#define WDT_CTRL_RESET_MODE_SOC (0x00 << 5) +#define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5) +#define WDT_CTRL_1MHZ_CLK BIT(4) +#define WDT_CTRL_WDT_EXT BIT(3) +#define WDT_CTRL_WDT_INTR BIT(2) +#define WDT_CTRL_RESET_SYSTEM BIT(1) +#define WDT_CTRL_ENABLE BIT(0) + +#define WDT_TIMEOUT_STATUS (0x10 / 4) +#define WDT_TIMEOUT_CLEAR (0x14 / 4) +#define WDT_RESET_WDITH (0x18 / 4) + +#define WDT_RESTART_MAGIC 0x4755 + +static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) +{ + return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; +} + +static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedWDTState *s = ASPEED_WDT(opaque); + + offset >>= 2; + + switch (offset) { + case WDT_STATUS: + return s->regs[WDT_STATUS]; + case WDT_RELOAD_VALUE: + return s->regs[WDT_RELOAD_VALUE]; + case WDT_RESTART: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from write-only reg at offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + return 0; + case WDT_CTRL: + return s->regs[WDT_CTRL]; + case WDT_TIMEOUT_STATUS: + case WDT_TIMEOUT_CLEAR: + case WDT_RESET_WDITH: + qemu_log_mask(LOG_UNIMP, + "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + +} + +static void aspeed_wdt_reload(AspeedWDTState *s, bool pclk) +{ + uint32_t reload; + + if (pclk) { + reload = muldiv64(s->regs[WDT_RELOAD_VALUE], NANOSECONDS_PER_SECOND, + s->pclk_freq); + } else { + reload = s->regs[WDT_RELOAD_VALUE] * 1000; + } + + if (aspeed_wdt_is_enabled(s)) { + timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload); + } +} + +static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedWDTState *s = ASPEED_WDT(opaque); + bool enable = data & WDT_CTRL_ENABLE; + + offset >>= 2; + + switch (offset) { + case WDT_STATUS: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only reg at offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + break; + case WDT_RELOAD_VALUE: + s->regs[WDT_RELOAD_VALUE] = data; + break; + case WDT_RESTART: + if ((data & 0xFFFF) == WDT_RESTART_MAGIC) { + s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE]; + aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); + } + break; + case WDT_CTRL: + if (enable && !aspeed_wdt_is_enabled(s)) { + s->regs[WDT_CTRL] = data; + aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); + } else if (!enable && aspeed_wdt_is_enabled(s)) { + s->regs[WDT_CTRL] = data; + timer_del(s->timer); + } + break; + case WDT_TIMEOUT_STATUS: + case WDT_TIMEOUT_CLEAR: + case WDT_RESET_WDITH: + qemu_log_mask(LOG_UNIMP, + "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + return; +} + +static WatchdogTimerModel model = { + .wdt_name = TYPE_ASPEED_WDT, + .wdt_description = "Aspeed watchdog device", +}; + +static const VMStateDescription vmstate_aspeed_wdt = { + .name = "vmstate_aspeed_wdt", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(timer, AspeedWDTState), + VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static const MemoryRegionOps aspeed_wdt_ops = { + .read = aspeed_wdt_read, + .write = aspeed_wdt_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void aspeed_wdt_reset(DeviceState *dev) +{ + AspeedWDTState *s = ASPEED_WDT(dev); + + s->regs[WDT_STATUS] = 0x3EF1480; + s->regs[WDT_RELOAD_VALUE] = 0x03EF1480; + s->regs[WDT_RESTART] = 0; + s->regs[WDT_CTRL] = 0; + + timer_del(s->timer); +} + +static void aspeed_wdt_timer_expired(void *dev) +{ + AspeedWDTState *s = ASPEED_WDT(dev); + + qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); + watchdog_perform_action(); + timer_del(s->timer); +} + +#define PCLK_HZ 24000000 + +static void aspeed_wdt_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedWDTState *s = ASPEED_WDT(dev); + + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); + + /* FIXME: This setting should be derived from the SCU hw strapping + * register SCU70 + */ + s->pclk_freq = PCLK_HZ; + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s, + TYPE_ASPEED_WDT, ASPEED_WDT_REGS_MAX * 4); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_wdt_realize; + dc->reset = aspeed_wdt_reset; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->vmsd = &vmstate_aspeed_wdt; +} + +static const TypeInfo aspeed_wdt_info = { + .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_wdt_class_init, +}; + +static void wdt_aspeed_register_types(void) +{ + watchdog_add_model(&model); + type_register_static(&aspeed_wdt_info); +} + +type_init(wdt_aspeed_register_types) diff --git a/include/disas/bfd.h b/include/disas/bfd.h index 0435b8c9f9..b01e002b4c 100644 --- a/include/disas/bfd.h +++ b/include/disas/bfd.h @@ -295,6 +295,7 @@ typedef struct disassemble_info { The bottom 16 bits are for the internal use of the disassembler. */ unsigned long flags; #define INSN_HAS_RELOC 0x80000000 +#define INSN_ARM_BE32 0x00010000 PTR private_data; /* Function used to get bytes to disassemble. MEMADDR is the @@ -306,6 +307,12 @@ typedef struct disassemble_info { (bfd_vma memaddr, bfd_byte *myaddr, int length, struct disassemble_info *info); + /* A place to stash the real read_memory_func if read_memory_func wants to + do some funky address arithmetic or similar (e.g. for ARM BE32 mode). */ + int (*read_memory_inner_func) + (bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info); + /* Function which should be called if we get an error that we can't recover from. STATUS is the errno value from read_memory_func and MEMADDR is the address that we were trying to read. INFO is a diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 1ab5deaa08..dbec0c1598 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -19,6 +19,7 @@ #include "hw/timer/aspeed_timer.h" #include "hw/i2c/aspeed_i2c.h" #include "hw/ssi/aspeed_smc.h" +#include "hw/watchdog/wdt_aspeed.h" #define ASPEED_SPIS_NUM 2 @@ -37,6 +38,7 @@ typedef struct AspeedSoCState { AspeedSMCState fmc; AspeedSMCState spi[ASPEED_SPIS_NUM]; AspeedSDMCState sdmc; + AspeedWDTState wdt; } AspeedSoCState; #define TYPE_ASPEED_SOC "aspeed-soc" diff --git a/include/hw/misc/unimp.h b/include/hw/misc/unimp.h new file mode 100644 index 0000000000..3462d85836 --- /dev/null +++ b/include/hw/misc/unimp.h @@ -0,0 +1,39 @@ +/* + * "Unimplemented" device + * + * Copyright Linaro Limited, 2017 + * Written by Peter Maydell + */ + +#ifndef HW_MISC_UNIMP_H +#define HW_MISC_UNIMP_H + +#define TYPE_UNIMPLEMENTED_DEVICE "unimplemented-device" + +/** + * create_unimplemented_device: create and map a dummy device + * @name: name of the device for debug logging + * @base: base address of the device's MMIO region + * @size: size of the device's MMIO region + * + * This utility function creates and maps an instance of unimplemented-device, + * which is a dummy device which simply logs all guest accesses to + * it via the qemu_log LOG_UNIMP debug log. + * The device is mapped at priority -1000, which means that you can + * use it to cover a large region and then map other devices on top of it + * if necessary. + */ +static inline void create_unimplemented_device(const char *name, + hwaddr base, + hwaddr size) +{ + DeviceState *dev = qdev_create(NULL, TYPE_UNIMPLEMENTED_DEVICE); + + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint64(dev, "size", size); + qdev_init_nofail(dev); + + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, base, -1000); +} + +#endif diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h new file mode 100644 index 0000000000..080c223122 --- /dev/null +++ b/include/hw/watchdog/wdt_aspeed.h @@ -0,0 +1,32 @@ +/* + * ASPEED Watchdog Controller + * + * Copyright (C) 2016-2017 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#ifndef ASPEED_WDT_H +#define ASPEED_WDT_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_WDT "aspeed.wdt" +#define ASPEED_WDT(obj) \ + OBJECT_CHECK(AspeedWDTState, (obj), TYPE_ASPEED_WDT) + +#define ASPEED_WDT_REGS_MAX (0x20 / 4) + +typedef struct AspeedWDTState { + /*< private >*/ + SysBusDevice parent_obj; + QEMUTimer *timer; + + /*< public >*/ + MemoryRegion iomem; + uint32_t regs[ASPEED_WDT_REGS_MAX]; + + uint32_t pclk_freq; +} AspeedWDTState; + +#endif /* ASPEED_WDT_H */ diff --git a/include/migration/migration.h b/include/migration/migration.h index af9135f0a7..7528cc2fbc 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -122,7 +122,6 @@ struct MigrationIncomingState { }; MigrationIncomingState *migration_incoming_get_current(void); -MigrationIncomingState *migration_incoming_state_new(QEMUFile *f); void migration_incoming_state_destroy(void); /* diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 3bbe3ed984..6233fe2e5b 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -985,17 +985,20 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque); +/* Returns: 0 on success, -1 on failure */ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, const VMStateDescription *vmsd, void *base, int alias_id, - int required_for_version); + int required_for_version, + Error **errp); +/* Returns: 0 on success, -1 on failure */ static inline int vmstate_register(DeviceState *dev, int instance_id, const VMStateDescription *vmsd, void *opaque) { return vmstate_register_with_alias_id(dev, instance_id, vmsd, - opaque, -1, 0); + opaque, -1, 0, NULL); } void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, diff --git a/include/qemu/option.h b/include/qemu/option.h index 1f9e3f939d..e786df0cfa 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -100,6 +100,15 @@ typedef int (*qemu_opt_loopfunc)(void *opaque, int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, Error **errp); +typedef struct { + QemuOpts *opts; + QemuOpt *opt; + const char *name; +} QemuOptsIter; + +void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name); +const char *qemu_opt_iter_next(QemuOptsIter *iter); + QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id); QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists, Error **errp); diff --git a/include/qom/cpu.h b/include/qom/cpu.h index ca4d0fb1b4..45bcf21a21 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -132,6 +132,8 @@ struct TranslationBlock; * @cpu_exec_exit: Callback for cpu_exec cleanup. * @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec. * @disas_set_info: Setup architecture specific components of disassembly info + * @adjust_watchpoint_address: Perform a target-specific adjustment to an + * address before attempting to match it against watchpoints. * * Represents a CPU family or model. */ @@ -195,6 +197,7 @@ typedef struct CPUClass { bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); void (*disas_set_info)(CPUState *cpu, disassemble_info *info); + vaddr (*adjust_watchpoint_address)(CPUState *cpu, vaddr addr, int len); } CPUClass; #ifdef HOST_WORDS_BIGENDIAN diff --git a/linux-user/hppa/target_syscall.h b/linux-user/hppa/target_syscall.h index ac18a9c575..e2f366839d 100644 --- a/linux-user/hppa/target_syscall.h +++ b/linux-user/hppa/target_syscall.h @@ -19,7 +19,7 @@ struct target_pt_regs { target_ulong ipsw; }; -#define UNAME_MACHINE "hppa" +#define UNAME_MACHINE "parisc" #define UNAME_MINIMUM_RELEASE "2.6.32" #define TARGET_CLONE_BACKWARDS #define TARGET_MINSIGSTKSZ 2048 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 11a311f9db..9be8e9530e 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9343,7 +9343,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_socket case TARGET_NR_socket: ret = do_socket(arg1, arg2, arg3); - fd_trans_unregister(ret); break; #endif #ifdef TARGET_NR_socketpair diff --git a/migration/migration.c b/migration/migration.c index 2766d2f586..2b179c69fa 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -111,32 +111,28 @@ MigrationState *migrate_get_current(void) return ¤t_migration; } -/* For incoming */ -static MigrationIncomingState *mis_current; - MigrationIncomingState *migration_incoming_get_current(void) { - return mis_current; -} - -MigrationIncomingState *migration_incoming_state_new(QEMUFile* f) -{ - mis_current = g_new0(MigrationIncomingState, 1); - mis_current->from_src_file = f; - mis_current->state = MIGRATION_STATUS_NONE; - QLIST_INIT(&mis_current->loadvm_handlers); - qemu_mutex_init(&mis_current->rp_mutex); - qemu_event_init(&mis_current->main_thread_load_event, false); + static bool once; + static MigrationIncomingState mis_current; - return mis_current; + if (!once) { + mis_current.state = MIGRATION_STATUS_NONE; + memset(&mis_current, 0, sizeof(MigrationIncomingState)); + QLIST_INIT(&mis_current.loadvm_handlers); + qemu_mutex_init(&mis_current.rp_mutex); + qemu_event_init(&mis_current.main_thread_load_event, false); + once = true; + } + return &mis_current; } void migration_incoming_state_destroy(void) { - qemu_event_destroy(&mis_current->main_thread_load_event); - loadvm_free_handlers(mis_current); - g_free(mis_current); - mis_current = NULL; + struct MigrationIncomingState *mis = migration_incoming_get_current(); + + qemu_event_destroy(&mis->main_thread_load_event); + loadvm_free_handlers(mis); } @@ -382,11 +378,11 @@ static void process_incoming_migration_bh(void *opaque) static void process_incoming_migration_co(void *opaque) { QEMUFile *f = opaque; - MigrationIncomingState *mis; + MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyState ps; int ret; - mis = migration_incoming_state_new(f); + mis->from_src_file = f; postcopy_state_set(POSTCOPY_INCOMING_NONE); migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_ACTIVE); @@ -1605,6 +1601,7 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running) QIOChannelBuffer *bioc; QEMUFile *fb; int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + bool restart_block = false; migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_POSTCOPY_ACTIVE); @@ -1624,6 +1621,7 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running) if (ret < 0) { goto fail; } + restart_block = true; /* * Cause any non-postcopiable, but iterative devices to @@ -1680,6 +1678,18 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running) /* <><> end of stuff going into the package */ + /* Last point of recovery; as soon as we send the package the destination + * can open devices and potentially start running. + * Lets just check again we've not got any errors. + */ + ret = qemu_file_get_error(ms->to_dst_file); + if (ret) { + error_report("postcopy_start: Migration stream errored (pre package)"); + goto fail_closefb; + } + + restart_block = false; + /* Now send that blob */ if (qemu_savevm_send_packaged(ms->to_dst_file, bioc->data, bioc->usage)) { goto fail_closefb; @@ -1717,6 +1727,17 @@ fail_closefb: fail: migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, MIGRATION_STATUS_FAILED); + if (restart_block) { + /* A failure happened early enough that we know the destination hasn't + * accessed block devices, so we're safe to recover. + */ + Error *local_err = NULL; + + bdrv_invalidate_cache_all(&local_err); + if (local_err) { + error_report_err(local_err); + } + } qemu_mutex_unlock_iothread(); return -1; } diff --git a/migration/savevm.c b/migration/savevm.c index 204012ecef..01997687c4 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -590,8 +590,14 @@ int register_savevm_live(DeviceState *dev, if (dev) { char *id = qdev_get_dev_path(dev); if (id) { - pstrcpy(se->idstr, sizeof(se->idstr), id); - pstrcat(se->idstr, sizeof(se->idstr), "/"); + if (snprintf(se->idstr, sizeof(se->idstr), "%s/", id) >= + sizeof(se->idstr)) { + error_report("Path too long for VMState (%s)", id); + g_free(id); + g_free(se); + + return -1; + } g_free(id); se->compat = g_new0(CompatEntry, 1); @@ -656,7 +662,8 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, - int required_for_version) + int required_for_version, + Error **errp) { SaveStateEntry *se; @@ -673,9 +680,14 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, if (dev) { char *id = qdev_get_dev_path(dev); if (id) { - pstrcpy(se->idstr, sizeof(se->idstr), id); - pstrcat(se->idstr, sizeof(se->idstr), "/"); - g_free(id); + if (snprintf(se->idstr, sizeof(se->idstr), "%s/", id) >= + sizeof(se->idstr)) { + error_setg(errp, "Path too long for VMState (%s)", id); + g_free(id); + g_free(se); + + return -1; + } se->compat = g_new0(CompatEntry, 1); pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), vmsd->name); @@ -1343,6 +1355,7 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis) } if (!postcopy_ram_supported_by_host()) { + postcopy_state_set(POSTCOPY_INCOMING_NONE); return -1; } @@ -2199,7 +2212,6 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-load-state"); f = qemu_fopen_channel_input(QIO_CHANNEL(ioc)); - migration_incoming_state_new(f); ret = qemu_loadvm_state(f); qemu_fclose(f); if (ret < 0) { @@ -2215,6 +2227,7 @@ int load_vmstate(const char *name) QEMUFile *f; int ret; AioContext *aio_context; + MigrationIncomingState *mis = migration_incoming_get_current(); if (!bdrv_all_can_snapshot(&bs)) { error_report("Device '%s' is writable but does not support snapshots.", @@ -2265,7 +2278,7 @@ int load_vmstate(const char *name) } qemu_system_reset(VMRESET_SILENT); - migration_incoming_state_new(f); + mis->from_src_file = f; aio_context_acquire(aio_context); ret = qemu_loadvm_state(f); diff --git a/qapi-schema.json b/qapi-schema.json index cbdffddbc6..61151f34d0 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1506,7 +1506,8 @@ # # The network connection information for server # -# @auth: #optional authentication method +# @auth: #optional authentication method used for +# the plain (non-websocket) VNC server # # Since: 2.1 ## @@ -1597,6 +1598,25 @@ 'tls-plain', 'x509-plain', 'tls-sasl', 'x509-sasl' ] } + +## +# @VncServerInfo2: +# +# The network connection information for server +# +# @auth: The current authentication type used by the servers +# +# @vencrypt: #optional The vencrypt sub authentication type used by the +# servers, only specified in case auth == vencrypt. +# +# Since: 2.9 +## +{ 'struct': 'VncServerInfo2', + 'base': 'VncBasicInfo', + 'data': { 'auth' : 'VncPrimaryAuth', + '*vencrypt' : 'VncVencryptSubAuth' } } + + ## # @VncInfo2: # @@ -1612,9 +1632,9 @@ # @clients: A list of @VncClientInfo of all currently connected clients. # The list can be empty, for obvious reasons. # -# @auth: The current authentication type used by the server +# @auth: The current authentication type used by the non-websockets servers # -# @vencrypt: #optional The vencrypt sub authentication type used by the server, +# @vencrypt: #optional The vencrypt authentication type used by the servers, # only specified in case auth == vencrypt. # # @display: #optional The display device the vnc server is linked to. @@ -1623,7 +1643,7 @@ ## { 'struct': 'VncInfo2', 'data': { 'id' : 'str', - 'server' : ['VncBasicInfo'], + 'server' : ['VncServerInfo2'], 'clients' : ['VncClientInfo'], 'auth' : 'VncPrimaryAuth', '*vencrypt' : 'VncVencryptSubAuth', diff --git a/qemu-options.hx b/qemu-options.hx index ad2f8fc873..ac036b4cbd 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1296,10 +1296,14 @@ is a TCP port number, not a display number. @item websocket Opens an additional TCP listening port dedicated to VNC Websocket connections. -By definition the Websocket port is 5700+@var{display}. If @var{host} is -specified connections will only be allowed from this host. -As an alternative the Websocket port could be specified by using -@code{websocket}=@var{port}. +If a bare @var{websocket} option is given, the Websocket port is +5700+@var{display}. An alternative port can be specified with the +syntax @code{websocket}=@var{port}. + +If @var{host} is specified connections will only be allowed from this host. +It is possible to control the websocket listen address independently, using +the syntax @code{websocket}=@var{host}:@var{port}. + If no TLS credentials are provided, the websocket connection runs in unencrypted mode. If TLS credentials are provided, the websocket connection requires encrypted client connections. @@ -391,6 +391,11 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu) return cpu->cpu_index; } +static vaddr cpu_adjust_watchpoint_address(CPUState *cpu, vaddr addr, int len) +{ + return addr; +} + static void cpu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -415,6 +420,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->cpu_exec_enter = cpu_common_noop; k->cpu_exec_exit = cpu_common_noop; k->cpu_exec_interrupt = cpu_common_exec_interrupt; + k->adjust_watchpoint_address = cpu_adjust_watchpoint_address; set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; @@ -374,7 +374,6 @@ define unnest-vars $(eval $(o:%.mo=%$(DSOSUF)): module-common.o $($o-objs)), $(error $o added in $v but $o-objs is not set))) $(shell mkdir -p ./ $(sort $(dir $($v)))) - $(shell cd $(BUILD_DIR) && mkdir -p ./ $(sort $(dir $($v)))) # Include all the .d files $(eval -include $(patsubst %.o,%.d,$(patsubst %.mo,%.d,$($v)))) $(eval $v := $(filter-out %/,$($v)))) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index de4d1c13d4..0f1aa63872 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -1,9 +1,10 @@ #!/bin/sh -# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390 program execution by the kernel +# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390/HPPA +# program execution by the kernel qemu_target_list="i386 i486 alpha arm sparc32plus ppc ppc64 ppc64le m68k \ mips mipsel mipsn32 mipsn32el mips64 mips64el \ -sh4 sh4eb s390x aarch64" +sh4 sh4eb s390x aarch64 hppa" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' @@ -91,6 +92,10 @@ aarch64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x aarch64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' aarch64_family=arm +hppa_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x0f' +hppa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +hppa_family=hppa + qemu_get_family() { cpu=${HOST_ARCH:-$(uname -m)} case "$cpu" in diff --git a/stubs/vmstate.c b/stubs/vmstate.c index 65906271d2..bbe158fe3b 100644 --- a/stubs/vmstate.c +++ b/stubs/vmstate.c @@ -8,7 +8,8 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, const VMStateDescription *vmsd, void *base, int alias_id, - int required_for_version) + int required_for_version, + Error **errp) { return 0; } diff --git a/target/arm/arm_ldst.h b/target/arm/arm_ldst.h index a76d89f62c..01587b3ebb 100644 --- a/target/arm/arm_ldst.h +++ b/target/arm/arm_ldst.h @@ -39,7 +39,15 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr, bool sctlr_b) { - uint16_t insn = cpu_lduw_code(env, addr); + uint16_t insn; +#ifndef CONFIG_USER_ONLY + /* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped + within each word. Undo that now. */ + if (sctlr_b) { + addr ^= 2; + } +#endif + insn = cpu_lduw_code(env, addr); if (bswap_code(sctlr_b)) { return bswap16(insn); } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e9f10f7747..4ee250cec6 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -446,6 +446,21 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info) return print_insn_arm(pc | 1, info); } +static int arm_read_memory_func(bfd_vma memaddr, bfd_byte *b, + int length, struct disassemble_info *info) +{ + assert(info->read_memory_inner_func); + assert((info->flags & INSN_ARM_BE32) == 0 || length == 2 || length == 4); + + if ((info->flags & INSN_ARM_BE32) != 0 && length == 2) { + assert(info->endian == BFD_ENDIAN_LITTLE); + return info->read_memory_inner_func(memaddr ^ 2, (bfd_byte *)b, 2, + info); + } else { + return info->read_memory_inner_func(memaddr, b, length, info); + } +} + static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) { ARMCPU *ac = ARM_CPU(cpu); @@ -471,6 +486,14 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) info->endian = BFD_ENDIAN_BIG; #endif } + if (info->read_memory_inner_func == NULL) { + info->read_memory_inner_func = info->read_memory_func; + info->read_memory_func = arm_read_memory_func; + } + info->flags &= ~INSN_ARM_BE32; + if (arm_sctlr_b(env)) { + info->flags |= INSN_ARM_BE32; + } } static void arm_cpu_initfn(Object *obj) @@ -541,6 +564,9 @@ static Property arm_cpu_has_el2_property = static Property arm_cpu_has_el3_property = DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); +static Property arm_cpu_cfgend_property = + DEFINE_PROP_BOOL("cfgend", ARMCPU, cfgend, false); + /* use property name "pmu" to match other archs and virt tools */ static Property arm_cpu_has_pmu_property = DEFINE_PROP_BOOL("pmu", ARMCPU, has_pmu, true); @@ -608,6 +634,8 @@ static void arm_cpu_post_init(Object *obj) } } + qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property, + &error_abort); } static void arm_cpu_finalizefn(Object *obj) @@ -728,6 +756,14 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->reset_sctlr |= (1 << 13); } + if (cpu->cfgend) { + if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { + cpu->reset_sctlr |= SCTLR_EE; + } else { + cpu->reset_sctlr |= SCTLR_B; + } + } + if (!cpu->has_el3) { /* If the has_el3 CPU property is disabled then we need to disable the * feature. @@ -1639,6 +1675,9 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_stop_before_watchpoint = true; cc->debug_excp_handler = arm_debug_excp_handler; cc->debug_check_watchpoint = arm_debug_check_watchpoint; +#if !defined(CONFIG_USER_ONLY) + cc->adjust_watchpoint_address = arm_adjust_watchpoint_address; +#endif cc->disas_set_info = arm_disas_set_info; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 39bff86daf..c0b3832d74 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -676,6 +676,13 @@ struct ARMCPU { int gic_vpribits; /* number of virtual priority bits */ int gic_vprebits; /* number of virtual preemption bits */ + /* Whether the cfgend input is high (i.e. this CPU should reset into + * big-endian mode). This setting isn't used directly: instead it modifies + * the reset_sctlr value to have SCTLR_B or SCTLR_EE set, depending on the + * architecture version. + */ + bool cfgend; + ARMELChangeHook *el_change_hook; void *el_change_hook_opaque; }; diff --git a/target/arm/internals.h b/target/arm/internals.h index 2e65bc12fa..f742a419ff 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -444,6 +444,11 @@ void hw_breakpoint_update_all(ARMCPU *cpu); /* Callback function for checking if a watchpoint should trigger. */ bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); +/* Adjust addresses (in BE32 mode) before testing against watchpoint + * addresses. + */ +vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len); + /* Callback function for when a watchpoint or breakpoint triggers. */ void arm_debug_excp_handler(CPUState *cs); diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index ba796d898e..fb366fdc35 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -1225,6 +1225,28 @@ bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) return check_watchpoints(cpu); } +vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + /* In BE32 system mode, target memory is stored byteswapped (on a + * little-endian host system), and by the time we reach here (via an + * opcode helper) the addresses of subword accesses have been adjusted + * to account for that, which means that watchpoints will not match. + * Undo the adjustment here. + */ + if (arm_sctlr_b(env)) { + if (len == 1) { + addr ^= 3; + } else if (len == 2) { + addr ^= 2; + } + } + + return addr; +} + void arm_debug_excp_handler(CPUState *cs) { /* Called by core code when a watchpoint or breakpoint fires; diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index d0352e2045..e61bbd6b3b 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -379,20 +379,6 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) } } -static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) -{ - /* We don't need to save all of the syndrome so we mask and shift - * out uneeded bits to help the sleb128 encoder do a better job. - */ - syn &= ARM_INSN_START_WORD2_MASK; - syn >>= ARM_INSN_START_WORD2_SHIFT; - - /* We check and clear insn_start_idx to catch multiple updates. */ - assert(s->insn_start_idx != 0); - tcg_set_insn_param(s->insn_start_idx, 2, syn); - s->insn_start_idx = 0; -} - static void unallocated_encoding(DisasContext *s) { /* Unallocated and reserved encodings are uncategorized */ diff --git a/target/arm/translate.c b/target/arm/translate.c index 493c627bcf..4436d8f3a2 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -102,6 +102,49 @@ void arm_translate_init(void) a64_translate_init(); } +/* Flags for the disas_set_da_iss info argument: + * lower bits hold the Rt register number, higher bits are flags. + */ +typedef enum ISSInfo { + ISSNone = 0, + ISSRegMask = 0x1f, + ISSInvalid = (1 << 5), + ISSIsAcqRel = (1 << 6), + ISSIsWrite = (1 << 7), + ISSIs16Bit = (1 << 8), +} ISSInfo; + +/* Save the syndrome information for a Data Abort */ +static void disas_set_da_iss(DisasContext *s, TCGMemOp memop, ISSInfo issinfo) +{ + uint32_t syn; + int sas = memop & MO_SIZE; + bool sse = memop & MO_SIGN; + bool is_acqrel = issinfo & ISSIsAcqRel; + bool is_write = issinfo & ISSIsWrite; + bool is_16bit = issinfo & ISSIs16Bit; + int srt = issinfo & ISSRegMask; + + if (issinfo & ISSInvalid) { + /* Some callsites want to conditionally provide ISS info, + * eg "only if this was not a writeback" + */ + return; + } + + if (srt == 15) { + /* For AArch32, insns where the src/dest is R15 never generate + * ISS information. Catching that here saves checking at all + * the call sites. + */ + return; + } + + syn = syn_data_abort_with_iss(0, sas, sse, srt, 0, is_acqrel, + 0, 0, 0, is_write, 0, is_16bit); + disas_set_insn_syndrome(s, syn); +} + static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s) { /* Return the mmu_idx to use for A32/T32 "unprivileged load/store" @@ -933,6 +976,14 @@ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 a32, int index) \ { \ gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data); \ +} \ +static inline void gen_aa32_ld##SUFF##_iss(DisasContext *s, \ + TCGv_i32 val, \ + TCGv_i32 a32, int index, \ + ISSInfo issinfo) \ +{ \ + gen_aa32_ld##SUFF(s, val, a32, index); \ + disas_set_da_iss(s, OPC, issinfo); \ } #define DO_GEN_ST(SUFF, OPC) \ @@ -940,6 +991,14 @@ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 a32, int index) \ { \ gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data); \ +} \ +static inline void gen_aa32_st##SUFF##_iss(DisasContext *s, \ + TCGv_i32 val, \ + TCGv_i32 a32, int index, \ + ISSInfo issinfo) \ +{ \ + gen_aa32_st##SUFF(s, val, a32, index); \ + disas_set_da_iss(s, OPC, issinfo | ISSIsWrite); \ } static inline void gen_aa32_frob64(DisasContext *s, TCGv_i64 val) @@ -8682,16 +8741,19 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (op1) { case 0: /* lda */ - gen_aa32_ld32u(s, tmp, addr, - get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, + get_mem_index(s), + rd | ISSIsAcqRel); break; case 2: /* ldab */ - gen_aa32_ld8u(s, tmp, addr, - get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, + get_mem_index(s), + rd | ISSIsAcqRel); break; case 3: /* ldah */ - gen_aa32_ld16u(s, tmp, addr, - get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, + get_mem_index(s), + rd | ISSIsAcqRel); break; default: abort(); @@ -8702,16 +8764,19 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = load_reg(s, rm); switch (op1) { case 0: /* stl */ - gen_aa32_st32(s, tmp, addr, - get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, + get_mem_index(s), + rm | ISSIsAcqRel); break; case 2: /* stlb */ - gen_aa32_st8(s, tmp, addr, - get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, + get_mem_index(s), + rm | ISSIsAcqRel); break; case 3: /* stlh */ - gen_aa32_st16(s, tmp, addr, - get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, + get_mem_index(s), + rm | ISSIsAcqRel); break; default: abort(); @@ -8782,11 +8847,18 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } else { int address_offset; bool load = insn & (1 << 20); + bool wbit = insn & (1 << 21); + bool pbit = insn & (1 << 24); bool doubleword = false; + ISSInfo issinfo; + /* Misc load/store */ rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; + /* ISS not valid if writeback */ + issinfo = (pbit & !wbit) ? rd : ISSInvalid; + if (!load && (sh & 2)) { /* doubleword */ ARCH(5TE); @@ -8799,8 +8871,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } addr = load_reg(s, rn); - if (insn & (1 << 24)) + if (pbit) { gen_add_datah_offset(s, insn, 0, addr); + } address_offset = 0; if (doubleword) { @@ -8829,30 +8902,33 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (sh) { case 1: - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), + issinfo); break; case 2: - gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8s_iss(s, tmp, addr, get_mem_index(s), + issinfo); break; default: case 3: - gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16s_iss(s, tmp, addr, get_mem_index(s), + issinfo); break; } } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), issinfo); tcg_temp_free_i32(tmp); } /* Perform base writeback before the loaded value to ensure correct behavior with overlapping index registers. ldrd with base writeback is undefined if the destination and index registers overlap. */ - if (!(insn & (1 << 24))) { + if (!pbit) { gen_add_datah_offset(s, insn, address_offset, addr); store_reg(s, rn, addr); - } else if (insn & (1 << 21)) { + } else if (wbit) { if (address_offset) tcg_gen_addi_i32(addr, addr, address_offset); store_reg(s, rn, addr); @@ -9195,17 +9271,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) /* load */ tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { - gen_aa32_ld8u(s, tmp, tmp2, i); + gen_aa32_ld8u_iss(s, tmp, tmp2, i, rd); } else { - gen_aa32_ld32u(s, tmp, tmp2, i); + gen_aa32_ld32u_iss(s, tmp, tmp2, i, rd); } } else { /* store */ tmp = load_reg(s, rd); if (insn & (1 << 22)) { - gen_aa32_st8(s, tmp, tmp2, i); + gen_aa32_st8_iss(s, tmp, tmp2, i, rd); } else { - gen_aa32_st32(s, tmp, tmp2, i); + gen_aa32_st32_iss(s, tmp, tmp2, i, rd); } tcg_temp_free_i32(tmp); } @@ -9666,13 +9742,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: /* ldab */ - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 1: /* ldah */ - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 2: /* lda */ - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; default: abort(); @@ -9682,13 +9761,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: /* stlb */ - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 1: /* stlh */ - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 2: /* stl */ - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; default: abort(); @@ -10634,6 +10716,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw int postinc = 0; int writeback = 0; int memidx; + ISSInfo issinfo; + if ((insn & 0x01100000) == 0x01000000) { if (disas_neon_ls_insn(s, insn)) { goto illegal_op; @@ -10737,24 +10821,27 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } } } + + issinfo = writeback ? ISSInvalid : rs; + if (insn & (1 << 20)) { /* Load. */ tmp = tcg_temp_new_i32(); switch (op) { case 0: - gen_aa32_ld8u(s, tmp, addr, memidx); + gen_aa32_ld8u_iss(s, tmp, addr, memidx, issinfo); break; case 4: - gen_aa32_ld8s(s, tmp, addr, memidx); + gen_aa32_ld8s_iss(s, tmp, addr, memidx, issinfo); break; case 1: - gen_aa32_ld16u(s, tmp, addr, memidx); + gen_aa32_ld16u_iss(s, tmp, addr, memidx, issinfo); break; case 5: - gen_aa32_ld16s(s, tmp, addr, memidx); + gen_aa32_ld16s_iss(s, tmp, addr, memidx, issinfo); break; case 2: - gen_aa32_ld32u(s, tmp, addr, memidx); + gen_aa32_ld32u_iss(s, tmp, addr, memidx, issinfo); break; default: tcg_temp_free_i32(tmp); @@ -10771,13 +10858,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: - gen_aa32_st8(s, tmp, addr, memidx); + gen_aa32_st8_iss(s, tmp, addr, memidx, issinfo); break; case 1: - gen_aa32_st16(s, tmp, addr, memidx); + gen_aa32_st16_iss(s, tmp, addr, memidx, issinfo); break; case 2: - gen_aa32_st32(s, tmp, addr, memidx); + gen_aa32_st32_iss(s, tmp, addr, memidx, issinfo); break; default: tcg_temp_free_i32(tmp); @@ -10914,7 +11001,8 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, val); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), + rd | ISSIs16Bit); tcg_temp_free_i32(addr); store_reg(s, rd, tmp); break; @@ -11117,28 +11205,28 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) switch (op) { case 0: /* str */ - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 1: /* strh */ - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 2: /* strb */ - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 3: /* ldrsb */ - gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 4: /* ldr */ - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 5: /* ldrh */ - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 6: /* ldrb */ - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 7: /* ldrsh */ - gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; } if (op >= 3) { /* load */ @@ -11182,12 +11270,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -11204,12 +11292,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -11225,12 +11313,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -11712,6 +11800,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) store_cpu_field(tmp, condexec_bits); } do { + dc->insn_start_idx = tcg_op_buf_count(); tcg_gen_insn_start(dc->pc, (dc->condexec_cond << 4) | (dc->condexec_mask >> 1), 0); diff --git a/target/arm/translate.h b/target/arm/translate.h index 285e96f087..abb0760158 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -104,6 +104,20 @@ static inline int default_exception_el(DisasContext *s) ? 3 : MAX(1, s->current_el); } +static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) +{ + /* We don't need to save all of the syndrome so we mask and shift + * out unneeded bits to help the sleb128 encoder do a better job. + */ + syn &= ARM_INSN_START_WORD2_MASK; + syn >>= ARM_INSN_START_WORD2_SHIFT; + + /* We check and clear insn_start_idx to catch multiple updates. */ + assert(s->insn_start_idx != 0); + tcg_set_insn_param(s->insn_start_idx, 2, syn); + s->insn_start_idx = 0; +} + /* target-specific extra values for is_jmp */ /* These instructions trap after executing, so the A32/T32 decoder must * defer them until after the conditional execution state has been updated. diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 413a5e12ad..c37a56f238 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -85,6 +85,7 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) break; case 35: env->iaoq_b = val; + break; case 59: env->cr26 = val; break; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4d243f7d3d..5d571f0a4e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1433,7 +1433,6 @@ static ExitStatus do_cbranch(DisasContext *ctx, target_long disp, bool is_n, target_ulong dest = iaoq_dest(ctx, disp); TCGLabel *taken = NULL; TCGCond c = cond->c; - int which = 0; bool n; assert(ctx->null_cond.c == TCG_COND_NEVER); @@ -1455,14 +1454,14 @@ static ExitStatus do_cbranch(DisasContext *ctx, target_long disp, bool is_n, n = is_n && disp < 0; if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, which++, ctx->iaoq_n, ctx->iaoq_n + 4); + gen_goto_tb(ctx, 0, ctx->iaoq_n, ctx->iaoq_n + 4); } else { if (!n && ctx->null_lab) { gen_set_label(ctx->null_lab); ctx->null_lab = NULL; } nullify_set(ctx, n); - gen_goto_tb(ctx, which++, ctx->iaoq_b, ctx->iaoq_n); + gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); } gen_set_label(taken); @@ -1471,23 +1470,17 @@ static ExitStatus do_cbranch(DisasContext *ctx, target_long disp, bool is_n, n = is_n && disp >= 0; if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, which++, dest, dest + 4); + gen_goto_tb(ctx, 1, dest, dest + 4); } else { nullify_set(ctx, n); - gen_goto_tb(ctx, which++, ctx->iaoq_b, dest); + gen_goto_tb(ctx, 1, ctx->iaoq_b, dest); } /* Not taken: the branch itself was nullified. */ if (ctx->null_lab) { gen_set_label(ctx->null_lab); ctx->null_lab = NULL; - if (which < 2) { - nullify_set(ctx, 0); - gen_goto_tb(ctx, which, ctx->iaoq_b, ctx->iaoq_n); - return EXIT_GOTO_TB; - } else { - return EXIT_IAQ_N_STALE; - } + return EXIT_IAQ_N_STALE; } else { return EXIT_GOTO_TB; } diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 98ca978b06..f7867771ae 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -128,29 +128,6 @@ static bool vnc_has_job_locked(VncState *vs) return false; } -bool vnc_has_job(VncState *vs) -{ - bool ret; - - vnc_lock_queue(queue); - ret = vnc_has_job_locked(vs); - vnc_unlock_queue(queue); - return ret; -} - -void vnc_jobs_clear(VncState *vs) -{ - VncJob *job, *tmp; - - vnc_lock_queue(queue); - QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) { - if (job->vs == vs || !vs) { - QTAILQ_REMOVE(&queue->jobs, job, next); - } - } - vnc_unlock_queue(queue); -} - void vnc_jobs_join(VncState *vs) { vnc_lock_queue(queue); diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h index 044bf9fbfd..59f66bcc35 100644 --- a/ui/vnc-jobs.h +++ b/ui/vnc-jobs.h @@ -34,8 +34,6 @@ VncJob *vnc_job_new(VncState *vs); int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h); void vnc_job_push(VncJob *job); -bool vnc_has_job(VncState *vs); -void vnc_jobs_clear(VncState *vs); void vnc_jobs_join(VncState *vs); void vnc_jobs_consume_buffer(VncState *vs); @@ -45,6 +45,7 @@ #include "crypto/tlscredsx509.h" #include "qom/object_interfaces.h" #include "qemu/cutils.h" +#include "io/dns-resolver.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -224,8 +225,12 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd) VncServerInfo *info; Error *err = NULL; + if (!vd->nlsock) { + return NULL; + } + info = g_malloc0(sizeof(*info)); - vnc_init_basic_info_from_server_addr(vd->lsock, + vnc_init_basic_info_from_server_addr(vd->lsock[0], qapi_VncServerInfo_base(info), &err); info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vd)); @@ -371,7 +376,7 @@ VncInfo *qmp_query_vnc(Error **errp) VncDisplay *vd = vnc_display_find(NULL); SocketAddress *addr = NULL; - if (vd == NULL || !vd->lsock) { + if (vd == NULL || !vd->nlsock) { info->enabled = false; } else { info->enabled = true; @@ -384,7 +389,7 @@ VncInfo *qmp_query_vnc(Error **errp) return info; } - addr = qio_channel_socket_get_local_address(vd->lsock, errp); + addr = qio_channel_socket_get_local_address(vd->lsock[0], errp); if (!addr) { goto out_error; } @@ -429,12 +434,20 @@ out_error: return NULL; } -static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc, - bool websocket, - VncBasicInfoList *prev) + +static void qmp_query_auth(int auth, int subauth, + VncPrimaryAuth *qmp_auth, + VncVencryptSubAuth *qmp_vencrypt, + bool *qmp_has_vencrypt); + +static VncServerInfo2List *qmp_query_server_entry(QIOChannelSocket *ioc, + bool websocket, + int auth, + int subauth, + VncServerInfo2List *prev) { - VncBasicInfoList *list; - VncBasicInfo *info; + VncServerInfo2List *list; + VncServerInfo2 *info; Error *err = NULL; SocketAddress *addr; @@ -444,85 +457,91 @@ static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc, return prev; } - info = g_new0(VncBasicInfo, 1); - vnc_init_basic_info(addr, info, &err); + info = g_new0(VncServerInfo2, 1); + vnc_init_basic_info(addr, qapi_VncServerInfo2_base(info), &err); qapi_free_SocketAddress(addr); if (err) { - qapi_free_VncBasicInfo(info); + qapi_free_VncServerInfo2(info); error_free(err); return prev; } info->websocket = websocket; - list = g_new0(VncBasicInfoList, 1); + qmp_query_auth(auth, subauth, &info->auth, + &info->vencrypt, &info->has_vencrypt); + + list = g_new0(VncServerInfo2List, 1); list->value = info; list->next = prev; return list; } -static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info) +static void qmp_query_auth(int auth, int subauth, + VncPrimaryAuth *qmp_auth, + VncVencryptSubAuth *qmp_vencrypt, + bool *qmp_has_vencrypt) { - switch (vd->auth) { + switch (auth) { case VNC_AUTH_VNC: - info->auth = VNC_PRIMARY_AUTH_VNC; + *qmp_auth = VNC_PRIMARY_AUTH_VNC; break; case VNC_AUTH_RA2: - info->auth = VNC_PRIMARY_AUTH_RA2; + *qmp_auth = VNC_PRIMARY_AUTH_RA2; break; case VNC_AUTH_RA2NE: - info->auth = VNC_PRIMARY_AUTH_RA2NE; + *qmp_auth = VNC_PRIMARY_AUTH_RA2NE; break; case VNC_AUTH_TIGHT: - info->auth = VNC_PRIMARY_AUTH_TIGHT; + *qmp_auth = VNC_PRIMARY_AUTH_TIGHT; break; case VNC_AUTH_ULTRA: - info->auth = VNC_PRIMARY_AUTH_ULTRA; + *qmp_auth = VNC_PRIMARY_AUTH_ULTRA; break; case VNC_AUTH_TLS: - info->auth = VNC_PRIMARY_AUTH_TLS; + *qmp_auth = VNC_PRIMARY_AUTH_TLS; break; case VNC_AUTH_VENCRYPT: - info->auth = VNC_PRIMARY_AUTH_VENCRYPT; - info->has_vencrypt = true; - switch (vd->subauth) { + *qmp_auth = VNC_PRIMARY_AUTH_VENCRYPT; + *qmp_has_vencrypt = true; + switch (subauth) { case VNC_AUTH_VENCRYPT_PLAIN: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN; break; case VNC_AUTH_VENCRYPT_TLSNONE: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE; break; case VNC_AUTH_VENCRYPT_TLSVNC: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC; break; case VNC_AUTH_VENCRYPT_TLSPLAIN: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN; break; case VNC_AUTH_VENCRYPT_X509NONE: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE; break; case VNC_AUTH_VENCRYPT_X509VNC: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC; break; case VNC_AUTH_VENCRYPT_X509PLAIN: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN; break; case VNC_AUTH_VENCRYPT_TLSSASL: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL; break; case VNC_AUTH_VENCRYPT_X509SASL: - info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL; + *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL; break; default: - info->has_vencrypt = false; + *qmp_has_vencrypt = false; break; } break; case VNC_AUTH_SASL: - info->auth = VNC_PRIMARY_AUTH_SASL; + *qmp_auth = VNC_PRIMARY_AUTH_SASL; break; case VNC_AUTH_NONE: default: - info->auth = VNC_PRIMARY_AUTH_NONE; + *qmp_auth = VNC_PRIMARY_AUTH_NONE; break; } } @@ -533,25 +552,28 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) VncInfo2 *info; VncDisplay *vd; DeviceState *dev; + size_t i; QTAILQ_FOREACH(vd, &vnc_displays, next) { info = g_new0(VncInfo2, 1); info->id = g_strdup(vd->id); info->clients = qmp_query_client_list(vd); - qmp_query_auth(vd, info); + qmp_query_auth(vd->auth, vd->subauth, &info->auth, + &info->vencrypt, &info->has_vencrypt); if (vd->dcl.con) { dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con), "device", NULL)); info->has_display = true; info->display = g_strdup(dev->id); } - if (vd->lsock != NULL) { + for (i = 0; i < vd->nlsock; i++) { info->server = qmp_query_server_entry( - vd->lsock, false, info->server); + vd->lsock[i], false, vd->auth, vd->subauth, info->server); } - if (vd->lwebsock != NULL) { + for (i = 0; i < vd->nlwebsock; i++) { info->server = qmp_query_server_entry( - vd->lwebsock, true, info->server); + vd->lwebsock[i], true, vd->ws_auth, + vd->ws_subauth, info->server); } item = g_new0(VncInfo2List, 1); @@ -1256,12 +1278,13 @@ ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp) if (ret <= 0) { if (ret == 0) { VNC_DEBUG("Closing down client sock: EOF\n"); + vnc_disconnect_start(vs); } else if (ret != QIO_CHANNEL_ERR_BLOCK) { VNC_DEBUG("Closing down client sock: ret %zd (%s)\n", ret, errp ? error_get_pretty(*errp) : "Unknown"); + vnc_disconnect_start(vs); } - vnc_disconnect_start(vs); if (errp) { error_free(*errp); *errp = NULL; @@ -3069,15 +3092,22 @@ static gboolean vnc_listen_io(QIOChannel *ioc, VncDisplay *vd = opaque; QIOChannelSocket *sioc = NULL; Error *err = NULL; + bool isWebsock = false; + size_t i; + + for (i = 0; i < vd->nlwebsock; i++) { + if (ioc == QIO_CHANNEL(vd->lwebsock[i])) { + isWebsock = true; + break; + } + } sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err); if (sioc != NULL) { qio_channel_set_name(QIO_CHANNEL(sioc), - ioc != QIO_CHANNEL(vd->lsock) ? - "vnc-ws-server" : "vnc-server"); + isWebsock ? "vnc-ws-server" : "vnc-server"); qio_channel_set_delay(QIO_CHANNEL(sioc), false); - vnc_connect(vd, sioc, false, - ioc != QIO_CHANNEL(vd->lsock)); + vnc_connect(vd, sioc, false, isWebsock); object_unref(OBJECT(sioc)); } else { /* client probably closed connection before we got there */ @@ -3137,24 +3167,33 @@ void vnc_display_init(const char *id) static void vnc_display_close(VncDisplay *vd) { + size_t i; if (!vd) { return; } vd->is_unix = false; - if (vd->lsock != NULL) { - if (vd->lsock_tag) { - g_source_remove(vd->lsock_tag); + for (i = 0; i < vd->nlsock; i++) { + if (vd->lsock_tag[i]) { + g_source_remove(vd->lsock_tag[i]); } - object_unref(OBJECT(vd->lsock)); - vd->lsock = NULL; + object_unref(OBJECT(vd->lsock[i])); } - if (vd->lwebsock != NULL) { - if (vd->lwebsock_tag) { - g_source_remove(vd->lwebsock_tag); + g_free(vd->lsock); + g_free(vd->lsock_tag); + vd->lsock = NULL; + vd->nlsock = 0; + + for (i = 0; i < vd->nlwebsock; i++) { + if (vd->lwebsock_tag[i]) { + g_source_remove(vd->lwebsock_tag[i]); } - object_unref(OBJECT(vd->lwebsock)); - vd->lwebsock = NULL; + object_unref(OBJECT(vd->lwebsock[i])); } + g_free(vd->lwebsock); + g_free(vd->lwebsock_tag); + vd->lwebsock = NULL; + vd->nlwebsock = 0; + vd->auth = VNC_AUTH_INVALID; vd->subauth = VNC_AUTH_INVALID; if (vd->tlscreds) { @@ -3204,7 +3243,11 @@ static void vnc_display_print_local_addr(VncDisplay *vd) SocketAddress *addr; Error *err = NULL; - addr = qio_channel_socket_get_local_address(vd->lsock, &err); + if (!vd->nlsock) { + return; + } + + addr = qio_channel_socket_get_local_address(vd->lsock[0], &err); if (!addr) { return; } @@ -3453,19 +3496,364 @@ vnc_display_create_creds(bool x509, } +static int vnc_display_get_address(const char *addrstr, + bool websocket, + int displaynum, + int to, + bool has_ipv4, + bool has_ipv6, + bool ipv4, + bool ipv6, + SocketAddress **retaddr, + Error **errp) +{ + int ret = -1; + SocketAddress *addr = NULL; + + addr = g_new0(SocketAddress, 1); + + if (strncmp(addrstr, "unix:", 5) == 0) { + addr->type = SOCKET_ADDRESS_KIND_UNIX; + addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + addr->u.q_unix.data->path = g_strdup(addrstr + 5); + + if (websocket) { + error_setg(errp, "UNIX sockets not supported with websock"); + goto cleanup; + } + + if (to) { + error_setg(errp, "Port range not support with UNIX socket"); + goto cleanup; + } + ret = 0; + } else { + const char *port; + size_t hostlen; + unsigned long long baseport = 0; + InetSocketAddress *inet; + + port = strrchr(addrstr, ':'); + if (!port) { + if (websocket) { + hostlen = 0; + port = addrstr; + } else { + error_setg(errp, "no vnc port specified"); + goto cleanup; + } + } else { + hostlen = port - addrstr; + port++; + if (*port == '\0') { + error_setg(errp, "vnc port cannot be empty"); + goto cleanup; + } + } + + addr->type = SOCKET_ADDRESS_KIND_INET; + inet = addr->u.inet.data = g_new0(InetSocketAddress, 1); + if (addrstr[0] == '[' && addrstr[hostlen - 1] == ']') { + inet->host = g_strndup(addrstr + 1, hostlen - 2); + } else { + inet->host = g_strndup(addrstr, hostlen); + } + /* plain VNC port is just an offset, for websocket + * port is absolute */ + if (websocket) { + if (g_str_equal(addrstr, "") || + g_str_equal(addrstr, "on")) { + if (displaynum == -1) { + error_setg(errp, "explicit websocket port is required"); + goto cleanup; + } + inet->port = g_strdup_printf( + "%d", displaynum + 5700); + if (to) { + inet->has_to = true; + inet->to = to + 5700; + } + } else { + inet->port = g_strdup(port); + } + } else { + if (parse_uint_full(port, &baseport, 10) < 0) { + error_setg(errp, "can't convert to a number: %s", port); + goto cleanup; + } + if (baseport > 65535 || + baseport + 5900 > 65535) { + error_setg(errp, "port %s out of range", port); + goto cleanup; + } + inet->port = g_strdup_printf( + "%d", (int)baseport + 5900); + + if (to) { + inet->has_to = true; + inet->to = to + 5900; + } + } + + inet->ipv4 = ipv4; + inet->has_ipv4 = has_ipv4; + inet->ipv6 = ipv6; + inet->has_ipv6 = has_ipv6; + + ret = baseport; + } + + *retaddr = addr; + + cleanup: + if (ret < 0) { + qapi_free_SocketAddress(addr); + } + return ret; +} + +static int vnc_display_get_addresses(QemuOpts *opts, + SocketAddress ***retsaddr, + size_t *retnsaddr, + SocketAddress ***retwsaddr, + size_t *retnwsaddr, + Error **errp) +{ + SocketAddress *saddr = NULL; + SocketAddress *wsaddr = NULL; + QemuOptsIter addriter; + const char *addr; + int to = qemu_opt_get_number(opts, "to", 0); + bool has_ipv4 = qemu_opt_get(opts, "ipv4"); + bool has_ipv6 = qemu_opt_get(opts, "ipv6"); + bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false); + bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false); + size_t i; + int displaynum = -1; + int ret = -1; + + *retsaddr = NULL; + *retnsaddr = 0; + *retwsaddr = NULL; + *retnwsaddr = 0; + + addr = qemu_opt_get(opts, "vnc"); + if (addr == NULL || g_str_equal(addr, "none")) { + ret = 0; + goto cleanup; + } + if (qemu_opt_get(opts, "websocket") && + !qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { + error_setg(errp, + "SHA1 hash support is required for websockets"); + goto cleanup; + } + + qemu_opt_iter_init(&addriter, opts, "vnc"); + while ((addr = qemu_opt_iter_next(&addriter)) != NULL) { + int rv; + rv = vnc_display_get_address(addr, false, 0, to, + has_ipv4, has_ipv6, + ipv4, ipv6, + &saddr, errp); + if (rv < 0) { + goto cleanup; + } + /* Historical compat - first listen address can be used + * to set the default websocket port + */ + if (displaynum == -1) { + displaynum = rv; + } + *retsaddr = g_renew(SocketAddress *, *retsaddr, *retnsaddr + 1); + (*retsaddr)[(*retnsaddr)++] = saddr; + } + + /* If we had multiple primary displays, we don't do defaults + * for websocket, and require explicit config instead. */ + if (*retnsaddr > 1) { + displaynum = -1; + } + + qemu_opt_iter_init(&addriter, opts, "websocket"); + while ((addr = qemu_opt_iter_next(&addriter)) != NULL) { + if (vnc_display_get_address(addr, true, displaynum, to, + has_ipv4, has_ipv6, + ipv4, ipv6, + &wsaddr, errp) < 0) { + goto cleanup; + } + + /* Historical compat - if only a single listen address was + * provided, then this is used to set the default listen + * address for websocket too + */ + if (*retnsaddr == 1 && + (*retsaddr)[0]->type == SOCKET_ADDRESS_KIND_INET && + wsaddr->type == SOCKET_ADDRESS_KIND_INET && + g_str_equal(wsaddr->u.inet.data->host, "") && + !g_str_equal((*retsaddr)[0]->u.inet.data->host, "")) { + g_free(wsaddr->u.inet.data->host); + wsaddr->u.inet.data->host = + g_strdup((*retsaddr)[0]->u.inet.data->host); + } + + *retwsaddr = g_renew(SocketAddress *, *retwsaddr, *retnwsaddr + 1); + (*retwsaddr)[(*retnwsaddr)++] = wsaddr; + } + + ret = 0; + cleanup: + if (ret < 0) { + for (i = 0; i < *retnsaddr; i++) { + qapi_free_SocketAddress((*retsaddr)[i]); + } + g_free(*retsaddr); + for (i = 0; i < *retnwsaddr; i++) { + qapi_free_SocketAddress((*retwsaddr)[i]); + } + g_free(*retwsaddr); + *retsaddr = *retwsaddr = NULL; + *retnsaddr = *retnwsaddr = 0; + } + return ret; +} + +static int vnc_display_connect(VncDisplay *vd, + SocketAddress **saddr, + size_t nsaddr, + SocketAddress **wsaddr, + size_t nwsaddr, + Error **errp) +{ + /* connect to viewer */ + QIOChannelSocket *sioc = NULL; + if (nwsaddr != 0) { + error_setg(errp, "Cannot use websockets in reverse mode"); + return -1; + } + if (nsaddr != 1) { + error_setg(errp, "Expected a single address in reverse mode"); + return -1; + } + vd->is_unix = saddr[0]->type == SOCKET_ADDRESS_KIND_UNIX; + sioc = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse"); + if (qio_channel_socket_connect_sync(sioc, saddr[0], errp) < 0) { + return -1; + } + vnc_connect(vd, sioc, false, false); + object_unref(OBJECT(sioc)); + return 0; +} + + +static int vnc_display_listen_addr(VncDisplay *vd, + SocketAddress *addr, + const char *name, + QIOChannelSocket ***lsock, + guint **lsock_tag, + size_t *nlsock, + Error **errp) +{ + QIODNSResolver *resolver = qio_dns_resolver_get_instance(); + SocketAddress **rawaddrs = NULL; + size_t nrawaddrs = 0; + Error *listenerr = NULL; + bool listening = false; + size_t i; + + if (qio_dns_resolver_lookup_sync(resolver, addr, &nrawaddrs, + &rawaddrs, errp) < 0) { + return -1; + } + + for (i = 0; i < nrawaddrs; i++) { + QIOChannelSocket *sioc = qio_channel_socket_new(); + + qio_channel_set_name(QIO_CHANNEL(sioc), name); + if (qio_channel_socket_listen_sync( + sioc, rawaddrs[i], listenerr == NULL ? &listenerr : NULL) < 0) { + continue; + } + listening = true; + (*nlsock)++; + *lsock = g_renew(QIOChannelSocket *, *lsock, *nlsock); + *lsock_tag = g_renew(guint, *lsock_tag, *nlsock); + + (*lsock)[*nlsock - 1] = sioc; + (*lsock_tag)[*nlsock - 1] = 0; + } + + for (i = 0; i < nrawaddrs; i++) { + qapi_free_SocketAddress(rawaddrs[i]); + } + g_free(rawaddrs); + + if (listenerr) { + if (!listening) { + error_propagate(errp, listenerr); + return -1; + } else { + error_free(listenerr); + } + } + + for (i = 0; i < *nlsock; i++) { + (*lsock_tag)[i] = qio_channel_add_watch( + QIO_CHANNEL((*lsock)[i]), + G_IO_IN, vnc_listen_io, vd, NULL); + } + + return 0; +} + + +static int vnc_display_listen(VncDisplay *vd, + SocketAddress **saddr, + size_t nsaddr, + SocketAddress **wsaddr, + size_t nwsaddr, + Error **errp) +{ + size_t i; + + for (i = 0; i < nsaddr; i++) { + if (vnc_display_listen_addr(vd, saddr[i], + "vnc-listen", + &vd->lsock, + &vd->lsock_tag, + &vd->nlsock, + errp) < 0) { + return -1; + } + } + for (i = 0; i < nwsaddr; i++) { + if (vnc_display_listen_addr(vd, wsaddr[i], + "vnc-ws-listen", + &vd->lwebsock, + &vd->lwebsock_tag, + &vd->nlwebsock, + errp) < 0) { + return -1; + } + } + + return 0; +} + + void vnc_display_open(const char *id, Error **errp) { VncDisplay *vd = vnc_display_find(id); QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id); - SocketAddress *saddr = NULL, *wsaddr = NULL; + SocketAddress **saddr = NULL, **wsaddr = NULL; + size_t nsaddr, nwsaddr; const char *share, *device_id; QemuConsole *con; bool password = false; bool reverse = false; - const char *vnc; - char *h; const char *credid; - int show_vnc_port = 0; bool sasl = false; #ifdef CONFIG_VNC_SASL int saslErr; @@ -3473,7 +3861,7 @@ void vnc_display_open(const char *id, Error **errp) int acl = 0; int lock_key_sync = 1; int key_delay_ms; - bool ws_enabled = false; + size_t i; if (!vd) { error_setg(errp, "VNC display not active"); @@ -3484,96 +3872,16 @@ void vnc_display_open(const char *id, Error **errp) if (!opts) { return; } - vnc = qemu_opt_get(opts, "vnc"); - if (!vnc || strcmp(vnc, "none") == 0) { - return; - } - - h = strrchr(vnc, ':'); - if (h) { - size_t hlen = h - vnc; - - const char *websocket = qemu_opt_get(opts, "websocket"); - int to = qemu_opt_get_number(opts, "to", 0); - bool has_ipv4 = qemu_opt_get(opts, "ipv4"); - bool has_ipv6 = qemu_opt_get(opts, "ipv6"); - bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false); - bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false); - - saddr = g_new0(SocketAddress, 1); - if (websocket) { - if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { - error_setg(errp, - "SHA1 hash support is required for websockets"); - goto fail; - } - - wsaddr = g_new0(SocketAddress, 1); - ws_enabled = true; - } - - if (strncmp(vnc, "unix:", 5) == 0) { - saddr->type = SOCKET_ADDRESS_KIND_UNIX; - saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); - saddr->u.q_unix.data->path = g_strdup(vnc + 5); - - if (ws_enabled) { - error_setg(errp, "UNIX sockets not supported with websock"); - goto fail; - } - } else { - unsigned long long baseport; - InetSocketAddress *inet; - saddr->type = SOCKET_ADDRESS_KIND_INET; - inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); - if (vnc[0] == '[' && vnc[hlen - 1] == ']') { - inet->host = g_strndup(vnc + 1, hlen - 2); - } else { - inet->host = g_strndup(vnc, hlen); - } - if (parse_uint_full(h + 1, &baseport, 10) < 0) { - error_setg(errp, "can't convert to a number: %s", h + 1); - goto fail; - } - if (baseport > 65535 || - baseport + 5900 > 65535) { - error_setg(errp, "port %s out of range", h + 1); - goto fail; - } - inet->port = g_strdup_printf( - "%d", (int)baseport + 5900); - - if (to) { - inet->has_to = true; - inet->to = to + 5900; - show_vnc_port = 1; - } - inet->ipv4 = ipv4; - inet->has_ipv4 = has_ipv4; - inet->ipv6 = ipv6; - inet->has_ipv6 = has_ipv6; - if (ws_enabled) { - wsaddr->type = SOCKET_ADDRESS_KIND_INET; - inet = wsaddr->u.inet.data = g_new0(InetSocketAddress, 1); - inet->host = g_strdup(saddr->u.inet.data->host); - inet->port = g_strdup(websocket); - - if (to) { - inet->has_to = true; - inet->to = to; - } - inet->ipv4 = ipv4; - inet->has_ipv4 = has_ipv4; - inet->ipv6 = ipv6; - inet->has_ipv6 = has_ipv6; - } - } - } else { - error_setg(errp, "no vnc port specified"); + if (vnc_display_get_addresses(opts, &saddr, &nsaddr, + &wsaddr, &nwsaddr, errp) < 0) { goto fail; } + if (saddr == NULL) { + return; + } + password = qemu_opt_get_bool(opts, "password", false); if (password) { if (fips_get_state()) { @@ -3760,63 +4068,31 @@ void vnc_display_open(const char *id, Error **errp) } if (reverse) { - /* connect to viewer */ - QIOChannelSocket *sioc = NULL; - vd->lsock = NULL; - vd->lwebsock = NULL; - if (ws_enabled) { - error_setg(errp, "Cannot use websockets in reverse mode"); - goto fail; - } - vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; - sioc = qio_channel_socket_new(); - qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse"); - if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) { + if (vnc_display_connect(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) { goto fail; } - vnc_connect(vd, sioc, false, false); - object_unref(OBJECT(sioc)); } else { - vd->lsock = qio_channel_socket_new(); - qio_channel_set_name(QIO_CHANNEL(vd->lsock), "vnc-listen"); - if (qio_channel_socket_listen_sync(vd->lsock, saddr, errp) < 0) { + if (vnc_display_listen(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) { goto fail; } - vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; - - if (ws_enabled) { - vd->lwebsock = qio_channel_socket_new(); - qio_channel_set_name(QIO_CHANNEL(vd->lwebsock), "vnc-ws-listen"); - if (qio_channel_socket_listen_sync(vd->lwebsock, - wsaddr, errp) < 0) { - object_unref(OBJECT(vd->lsock)); - vd->lsock = NULL; - goto fail; - } - } - - vd->lsock_tag = qio_channel_add_watch( - QIO_CHANNEL(vd->lsock), - G_IO_IN, vnc_listen_io, vd, NULL); - if (ws_enabled) { - vd->lwebsock_tag = qio_channel_add_watch( - QIO_CHANNEL(vd->lwebsock), - G_IO_IN, vnc_listen_io, vd, NULL); - } } - if (show_vnc_port) { + if (qemu_opt_get(opts, "to")) { vnc_display_print_local_addr(vd); } - qapi_free_SocketAddress(saddr); - qapi_free_SocketAddress(wsaddr); + cleanup: + for (i = 0; i < nsaddr; i++) { + qapi_free_SocketAddress(saddr[i]); + } + for (i = 0; i < nwsaddr; i++) { + qapi_free_SocketAddress(wsaddr[i]); + } return; fail: - qapi_free_SocketAddress(saddr); - qapi_free_SocketAddress(wsaddr); - ws_enabled = false; + vnc_display_close(vd); + goto cleanup; } void vnc_display_add_client(const char *id, int csock, bool skipauth) @@ -146,10 +146,12 @@ struct VncDisplay int num_exclusive; int connections_limit; VncSharePolicy share_policy; - QIOChannelSocket *lsock; - guint lsock_tag; - QIOChannelSocket *lwebsock; - guint lwebsock_tag; + size_t nlsock; + QIOChannelSocket **lsock; + guint *lsock_tag; + size_t nlwebsock; + QIOChannelSocket **lwebsock; + guint *lwebsock_tag; DisplaySurface *ds; DisplayChangeListener dcl; kbd_layout_t *kbd_layout; diff --git a/util/qemu-option.c b/util/qemu-option.c index 3467dc2397..d611946333 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -332,6 +332,25 @@ const char *qemu_opt_get(QemuOpts *opts, const char *name) return opt ? opt->str : NULL; } +void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name) +{ + iter->opts = opts; + iter->opt = QTAILQ_FIRST(&opts->head); + iter->name = name; +} + +const char *qemu_opt_iter_next(QemuOptsIter *iter) +{ + QemuOpt *ret = iter->opt; + if (iter->name) { + while (ret && !g_str_equal(iter->name, ret->name)) { + ret = QTAILQ_NEXT(ret, next); + } + } + iter->opt = ret ? QTAILQ_NEXT(ret, next) : NULL; + return ret ? ret->str : NULL; +} + /* Get a known option (or its default) and remove it from the list * all in one action. Return a malloced string of the option value. * Result must be freed by caller with g_free(). |