diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | chardev/char-pty.c | 2 | ||||
-rw-r--r-- | chardev/char-socket.c | 10 | ||||
-rw-r--r-- | cpu-exec.c | 86 | ||||
-rw-r--r-- | cpus.c | 42 | ||||
-rw-r--r-- | gdbstub.c | 209 | ||||
-rw-r--r-- | hw/char/mcf_uart.c | 102 | ||||
-rw-r--r-- | hw/i386/kvm/ioapic.c | 13 | ||||
-rw-r--r-- | hw/intc/apic_common.c | 2 | ||||
-rw-r--r-- | hw/intc/ioapic.c | 6 | ||||
-rw-r--r-- | hw/m68k/mcf5208.c | 6 | ||||
-rw-r--r-- | hw/misc/pvpanic.c | 2 | ||||
-rw-r--r-- | hw/ppc/spapr_rtas.c | 3 | ||||
-rw-r--r-- | include/exec/exec-all.h | 1 | ||||
-rw-r--r-- | include/hw/m68k/mcf.h | 6 | ||||
-rw-r--r-- | include/qom/cpu.h | 10 | ||||
-rw-r--r-- | include/sysemu/sysemu.h | 4 | ||||
-rw-r--r-- | kvm-all.c | 3 | ||||
-rw-r--r-- | qapi-schema.json | 24 | ||||
-rw-r--r-- | qapi/event.json | 6 | ||||
-rw-r--r-- | qemu-nbd.c | 172 | ||||
-rw-r--r-- | qemu-options.hx | 4 | ||||
-rw-r--r-- | qom/cpu.c | 11 | ||||
-rw-r--r-- | target/i386/cpu.c | 51 | ||||
-rw-r--r-- | target/i386/cpu.h | 5 | ||||
-rw-r--r-- | target/i386/excp_helper.c | 11 | ||||
-rw-r--r-- | target/i386/helper.h | 1 | ||||
-rw-r--r-- | target/i386/misc_helper.c | 24 | ||||
-rw-r--r-- | target/i386/seg_helper.c | 6 | ||||
-rw-r--r-- | target/i386/svm_helper.c | 65 | ||||
-rw-r--r-- | target/s390x/kvm.c | 4 | ||||
-rw-r--r-- | tests/test-vmstate.c | 11 | ||||
-rw-r--r-- | vl.c | 56 |
33 files changed, 711 insertions, 253 deletions
@@ -299,7 +299,11 @@ qemu-version.h: FORCE printf '""\n'; \ fi; \ fi) > $@.tmp) - $(call quiet-command, cmp -s $@ $@.tmp || mv $@.tmp $@) + $(call quiet-command, if ! cmp -s $@ $@.tmp; then \ + mv $@.tmp $@; \ + else \ + rm $@.tmp; \ + fi) config-host.h: config-host.h-timestamp config-host.h-timestamp: config-host.mak diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 27eb85f..ecf2c7a 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -129,7 +129,7 @@ static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len) /* guest sends data, check for (re-)connect */ pty_chr_update_read_handler_locked(chr); if (!s->connected) { - return 0; + return len; } } return io_channel_send(s->ioc, buf, len); diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 4068dc5..865c527 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -97,6 +97,9 @@ static gboolean tcp_chr_accept(QIOChannel *chan, GIOCondition cond, void *opaque); +static int tcp_chr_read_poll(void *opaque); +static void tcp_chr_disconnect(Chardev *chr); + /* Called with chr_write_lock held. */ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) { @@ -114,6 +117,13 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) s->write_msgfds_num = 0; } + if (ret < 0 && errno != EAGAIN) { + if (tcp_chr_read_poll(chr) <= 0) { + tcp_chr_disconnect(chr); + return len; + } /* else let the read handler finish it properly */ + } + return ret; } else { /* XXX: indicate an error ? */ @@ -461,7 +461,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) return false; } -static inline void cpu_handle_interrupt(CPUState *cpu, +static inline bool cpu_handle_interrupt(CPUState *cpu, TranslationBlock **last_tb) { CPUClass *cc = CPU_GET_CLASS(cpu); @@ -475,7 +475,7 @@ static inline void cpu_handle_interrupt(CPUState *cpu, if (interrupt_request & CPU_INTERRUPT_DEBUG) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; - cpu_loop_exit(cpu); + return true; } if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ @@ -484,23 +484,23 @@ static inline void cpu_handle_interrupt(CPUState *cpu, cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; cpu->exception_index = EXCP_HLT; - cpu_loop_exit(cpu); + return true; } #if defined(TARGET_I386) else if (interrupt_request & CPU_INTERRUPT_INIT) { X86CPU *x86_cpu = X86_CPU(cpu); CPUArchState *env = &x86_cpu->env; replay_interrupt(); - cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); do_cpu_init(x86_cpu); cpu->exception_index = EXCP_HALTED; - cpu_loop_exit(cpu); + return true; } #else else if (interrupt_request & CPU_INTERRUPT_RESET) { replay_interrupt(); cpu_reset(cpu); - cpu_loop_exit(cpu); + return true; } #endif /* The target hook has 3 exit conditions: @@ -526,8 +526,10 @@ static inline void cpu_handle_interrupt(CPUState *cpu, if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) { atomic_set(&cpu->exit_request, 0); cpu->exception_index = EXCP_INTERRUPT; - cpu_loop_exit(cpu); + return true; } + + return false; } static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, @@ -542,7 +544,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, trace_exec_tb(tb, tb->pc); ret = cpu_tb_exec(cpu, tb); - *last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); + tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); *tb_exit = ret & TB_EXIT_MASK; switch (*tb_exit) { case TB_EXIT_REQUESTED: @@ -552,11 +554,11 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, * have set something else (eg exit_request or * interrupt_request) which we will handle * next time around the loop. But we need to - * ensure the tcg_exit_req read in generated code + * ensure the zeroing of tcg_exit_req (see cpu_tb_exec) * comes before the next read of cpu->exit_request * or cpu->interrupt_request. */ - smp_rmb(); + smp_mb(); *last_tb = NULL; break; case TB_EXIT_ICOUNT_EXPIRED: @@ -566,6 +568,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, abort(); #else int insns_left = cpu->icount_decr.u32; + *last_tb = NULL; if (cpu->icount_extra && insns_left >= 0) { /* Refill decrementer and continue execution. */ cpu->icount_extra += insns_left; @@ -575,17 +578,17 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, } else { if (insns_left > 0) { /* Execute remaining instructions. */ - cpu_exec_nocache(cpu, insns_left, *last_tb, false); + cpu_exec_nocache(cpu, insns_left, tb, false); align_clocks(sc, cpu); } cpu->exception_index = EXCP_INTERRUPT; - *last_tb = NULL; cpu_loop_exit(cpu); } break; #endif } default: + *last_tb = tb; break; } } @@ -621,42 +624,37 @@ int cpu_exec(CPUState *cpu) */ init_delay_params(&sc, cpu); - for(;;) { - /* prepare setjmp context for exception handling */ - if (sigsetjmp(cpu->jmp_env, 0) == 0) { - TranslationBlock *tb, *last_tb = NULL; - int tb_exit = 0; - - /* if an exception is pending, we execute it here */ - if (cpu_handle_exception(cpu, &ret)) { - break; - } - - for(;;) { - cpu_handle_interrupt(cpu, &last_tb); - tb = tb_find(cpu, last_tb, tb_exit); - cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit, &sc); - /* Try to align the host and virtual clocks - if the guest is in advance */ - align_clocks(&sc, cpu); - } /* for(;;) */ - } else { + /* prepare setjmp context for exception handling */ + if (sigsetjmp(cpu->jmp_env, 0) != 0) { #if defined(__clang__) || !QEMU_GNUC_PREREQ(4, 6) - /* Some compilers wrongly smash all local variables after - * siglongjmp. There were bug reports for gcc 4.5.0 and clang. - * Reload essential local variables here for those compilers. - * Newer versions of gcc would complain about this code (-Wclobbered). */ - cpu = current_cpu; - cc = CPU_GET_CLASS(cpu); + /* Some compilers wrongly smash all local variables after + * siglongjmp. There were bug reports for gcc 4.5.0 and clang. + * Reload essential local variables here for those compilers. + * Newer versions of gcc would complain about this code (-Wclobbered). */ + cpu = current_cpu; + cc = CPU_GET_CLASS(cpu); #else /* buggy compiler */ - /* Assert that the compiler does not smash local variables. */ - g_assert(cpu == current_cpu); - g_assert(cc == CPU_GET_CLASS(cpu)); + /* Assert that the compiler does not smash local variables. */ + g_assert(cpu == current_cpu); + g_assert(cc == CPU_GET_CLASS(cpu)); #endif /* buggy compiler */ - cpu->can_do_io = 1; - tb_lock_reset(); + cpu->can_do_io = 1; + tb_lock_reset(); + } + + /* if an exception is pending, we execute it here */ + while (!cpu_handle_exception(cpu, &ret)) { + TranslationBlock *last_tb = NULL; + int tb_exit = 0; + + while (!cpu_handle_interrupt(cpu, &last_tb)) { + TranslationBlock *tb = tb_find(cpu, last_tb, tb_exit); + cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit, &sc); + /* Try to align the host and virtual clocks + if the guest is in advance */ + align_clocks(&sc, cpu); } - } /* for(;;) */ + } cc->cpu_exec_exit(cpu); rcu_read_unlock(); @@ -1578,6 +1578,48 @@ int vm_stop(RunState state) return do_vm_stop(state); } +/** + * Prepare for (re)starting the VM. + * Returns -1 if the vCPUs are not to be restarted (e.g. if they are already + * running or in case of an error condition), 0 otherwise. + */ +int vm_prepare_start(void) +{ + RunState requested; + int res = 0; + + qemu_vmstop_requested(&requested); + if (runstate_is_running() && requested == RUN_STATE__MAX) { + return -1; + } + + /* Ensure that a STOP/RESUME pair of events is emitted if a + * vmstop request was pending. The BLOCK_IO_ERROR event, for + * example, according to documentation is always followed by + * the STOP event. + */ + if (runstate_is_running()) { + qapi_event_send_stop(&error_abort); + res = -1; + } else { + replay_enable_events(); + cpu_enable_ticks(); + runstate_set(RUN_STATE_RUNNING); + vm_state_notify(1, RUN_STATE_RUNNING); + } + + /* We are sending this now, but the CPUs will be resumed shortly later */ + qapi_event_send_resume(&error_abort); + return res; +} + +void vm_start(void) +{ + if (!vm_prepare_start()) { + resume_all_vcpus(); + } +} + /* does a state transition even if the VM is already stopped, current state is forgotten forever */ int vm_stop_force_state(RunState state) @@ -387,6 +387,60 @@ static inline void gdb_continue(GDBState *s) #endif } +/* + * Resume execution, per CPU actions. For user-mode emulation it's + * equivalent to gdb_continue. + */ +static int gdb_continue_partial(GDBState *s, char *newstates) +{ + CPUState *cpu; + int res = 0; +#ifdef CONFIG_USER_ONLY + /* + * This is not exactly accurate, but it's an improvement compared to the + * previous situation, where only one CPU would be single-stepped. + */ + CPU_FOREACH(cpu) { + if (newstates[cpu->cpu_index] == 's') { + cpu_single_step(cpu, sstep_flags); + } + } + s->running_state = 1; +#else + int flag = 0; + + if (!runstate_needs_reset()) { + if (vm_prepare_start()) { + return 0; + } + + CPU_FOREACH(cpu) { + switch (newstates[cpu->cpu_index]) { + case 0: + case 1: + break; /* nothing to do here */ + case 's': + cpu_single_step(cpu, sstep_flags); + cpu_resume(cpu); + flag = 1; + break; + case 'c': + cpu_resume(cpu); + flag = 1; + break; + default: + res = -1; + break; + } + } + } + if (flag) { + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); + } +#endif + return res; +} + static void put_buffer(GDBState *s, const uint8_t *buf, int len) { #ifdef CONFIG_USER_ONLY @@ -785,6 +839,107 @@ static int is_query_packet(const char *p, const char *query, char separator) (p[query_len] == '\0' || p[query_len] == separator); } +/** + * gdb_handle_vcont - Parses and handles a vCont packet. + * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is + * a format error, 0 on success. + */ +static int gdb_handle_vcont(GDBState *s, const char *p) +{ + int res, idx, signal = 0; + char cur_action; + char *newstates; + unsigned long tmp; + CPUState *cpu; +#ifdef CONFIG_USER_ONLY + int max_cpus = 1; /* global variable max_cpus exists only in system mode */ + + CPU_FOREACH(cpu) { + max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus; + } +#endif + /* uninitialised CPUs stay 0 */ + newstates = g_new0(char, max_cpus); + + /* mark valid CPUs with 1 */ + CPU_FOREACH(cpu) { + newstates[cpu->cpu_index] = 1; + } + + /* + * res keeps track of what error we are returning, with -ENOTSUP meaning + * that the command is unknown or unsupported, thus returning an empty + * packet, while -EINVAL and -ERANGE cause an E22 packet, due to invalid, + * or incorrect parameters passed. + */ + res = 0; + while (*p) { + if (*p++ != ';') { + res = -ENOTSUP; + goto out; + } + + cur_action = *p++; + if (cur_action == 'C' || cur_action == 'S') { + cur_action = tolower(cur_action); + res = qemu_strtoul(p + 1, &p, 16, &tmp); + if (res) { + goto out; + } + signal = gdb_signal_to_target(tmp); + } else if (cur_action != 'c' && cur_action != 's') { + /* unknown/invalid/unsupported command */ + res = -ENOTSUP; + goto out; + } + /* thread specification. special values: (none), -1 = all; 0 = any */ + if ((p[0] == ':' && p[1] == '-' && p[2] == '1') || (p[0] != ':')) { + if (*p == ':') { + p += 3; + } + for (idx = 0; idx < max_cpus; idx++) { + if (newstates[idx] == 1) { + newstates[idx] = cur_action; + } + } + } else if (*p == ':') { + p++; + res = qemu_strtoul(p, &p, 16, &tmp); + if (res) { + goto out; + } + idx = tmp; + /* 0 means any thread, so we pick the first valid CPU */ + if (!idx) { + idx = cpu_index(first_cpu); + } + + /* + * If we are in user mode, the thread specified is actually a + * thread id, and not an index. We need to find the actual + * CPU first, and only then we can use its index. + */ + cpu = find_cpu(idx); + /* invalid CPU/thread specified */ + if (!idx || !cpu) { + res = -EINVAL; + goto out; + } + /* only use if no previous match occourred */ + if (newstates[cpu->cpu_index] == 1) { + newstates[cpu->cpu_index] = cur_action; + } + } + } + s->signal = signal; + gdb_continue_partial(s, newstates); + +out: + g_free(newstates); + + return res; +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -830,60 +985,20 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) return RS_IDLE; case 'v': if (strncmp(p, "Cont", 4) == 0) { - int res_signal, res_thread; - p += 4; if (*p == '?') { put_packet(s, "vCont;c;C;s;S"); break; } - res = 0; - res_signal = 0; - res_thread = 0; - while (*p) { - int action, signal; - - if (*p++ != ';') { - res = 0; - break; - } - action = *p++; - signal = 0; - if (action == 'C' || action == 'S') { - signal = gdb_signal_to_target(strtoul(p, (char **)&p, 16)); - if (signal == -1) { - signal = 0; - } - } else if (action != 'c' && action != 's') { - res = 0; - break; - } - thread = 0; - if (*p == ':') { - thread = strtoull(p+1, (char **)&p, 16); - } - action = tolower(action); - if (res == 0 || (res == 'c' && action == 's')) { - res = action; - res_signal = signal; - res_thread = thread; - } - } + + res = gdb_handle_vcont(s, p); + if (res) { - if (res_thread != -1 && res_thread != 0) { - cpu = find_cpu(res_thread); - if (cpu == NULL) { - put_packet(s, "E22"); - break; - } - s->c_cpu = cpu; - } - if (res == 's') { - cpu_single_step(s->c_cpu, sstep_flags); + if ((res == -EINVAL) || (res == -ERANGE)) { + put_packet(s, "E22"); + break; } - s->signal = res_signal; - gdb_continue(s); - return RS_IDLE; + goto unknown_command; } break; } else { diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 80c380e..e69672f 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -7,12 +7,15 @@ */ #include "qemu/osdep.h" #include "hw/hw.h" +#include "hw/sysbus.h" #include "hw/m68k/mcf.h" #include "sysemu/char.h" #include "exec/address-spaces.h" #include "qapi/error.h" typedef struct { + SysBusDevice parent_obj; + MemoryRegion iomem; uint8_t mr[2]; uint8_t sr; @@ -30,6 +33,9 @@ typedef struct { CharBackend chr; } mcf_uart_state; +#define TYPE_MCF_UART "mcf-uart" +#define MCF_UART(obj) OBJECT_CHECK(mcf_uart_state, (obj), TYPE_MCF_UART) + /* UART Status Register bits. */ #define MCF_UART_RxRDY 0x01 #define MCF_UART_FFULL 0x02 @@ -220,8 +226,10 @@ void mcf_uart_write(void *opaque, hwaddr addr, mcf_uart_update(s); } -static void mcf_uart_reset(mcf_uart_state *s) +static void mcf_uart_reset(DeviceState *dev) { + mcf_uart_state *s = MCF_UART(dev); + s->fifo_len = 0; s->mr[0] = 0; s->mr[1] = 0; @@ -275,36 +283,80 @@ static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) mcf_uart_push_byte(s, buf[0]); } -void *mcf_uart_init(qemu_irq irq, Chardev *chr) -{ - mcf_uart_state *s; - - s = g_malloc0(sizeof(mcf_uart_state)); - s->irq = irq; - if (chr) { - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, - mcf_uart_receive, mcf_uart_event, - s, NULL, true); - } - mcf_uart_reset(s); - return s; -} - static const MemoryRegionOps mcf_uart_ops = { .read = mcf_uart_read, .write = mcf_uart_write, .endianness = DEVICE_NATIVE_ENDIAN, }; -void mcf_uart_mm_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, - Chardev *chr) +static void mcf_uart_instance_init(Object *obj) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + mcf_uart_state *s = MCF_UART(dev); + + memory_region_init_io(&s->iomem, obj, &mcf_uart_ops, s, "uart", 0x40); + sysbus_init_mmio(dev, &s->iomem); + + sysbus_init_irq(dev, &s->irq); +} + +static void mcf_uart_realize(DeviceState *dev, Error **errp) +{ + mcf_uart_state *s = MCF_UART(dev); + + qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, mcf_uart_receive, + mcf_uart_event, s, NULL, true); +} + +static Property mcf_uart_properties[] = { + DEFINE_PROP_CHR("chardev", mcf_uart_state, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mcf_uart_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = mcf_uart_realize; + dc->reset = mcf_uart_reset; + dc->props = mcf_uart_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo mcf_uart_info = { + .name = TYPE_MCF_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(mcf_uart_state), + .instance_init = mcf_uart_instance_init, + .class_init = mcf_uart_class_init, +}; + +static void mcf_uart_register(void) +{ + type_register_static(&mcf_uart_info); +} + +type_init(mcf_uart_register) + +void *mcf_uart_init(qemu_irq irq, Chardev *chrdrv) +{ + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_MCF_UART); + if (chrdrv) { + qdev_prop_set_chr(dev, "chardev", chrdrv); + } + qdev_init_nofail(dev); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + + return dev; +} + +void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chrdrv) { - mcf_uart_state *s; + DeviceState *dev; - s = mcf_uart_init(irq, chr); - memory_region_init_io(&s->iomem, NULL, &mcf_uart_ops, s, "uart", 0x40); - memory_region_add_subregion(sysmem, base, &s->iomem); + dev = mcf_uart_init(irq, chrdrv); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); } diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 8eb2c7a..98ca480 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -114,11 +114,11 @@ static void kvm_ioapic_put(IOAPICCommonState *s) void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict) { - IOAPICCommonState s; + IOAPICCommonState *s = IOAPIC_COMMON(object_resolve_path("ioapic", NULL)); - kvm_ioapic_get(&s); - - ioapic_print_redtbl(mon, &s); + assert(s); + kvm_ioapic_get(s); + ioapic_print_redtbl(mon, s); } static void kvm_ioapic_reset(DeviceState *dev) @@ -143,6 +143,11 @@ static void kvm_ioapic_realize(DeviceState *dev, Error **errp) IOAPICCommonState *s = IOAPIC_COMMON(dev); memory_region_init_reservation(&s->io_memory, NULL, "kvm-ioapic", 0x1000); + /* + * KVM ioapic only supports 0x11 now. This will only be used when + * we want to dump ioapic version. + */ + s->version = 0x11; qdev_init_gpio_in(dev, kvm_ioapic_set_irq, IOAPIC_NUM_PINS); } diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 6ce8ef7..7a6e771 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -251,6 +251,8 @@ static void apic_reset_common(DeviceState *dev) s->apicbase = APIC_DEFAULT_ADDRESS | bsp | MSR_IA32_APICBASE_ENABLE; s->id = s->initial_apic_id; + apic_reset_irq_delivered(); + s->vapic_paddr = 0; info->vapic_base_update(s); diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 9047b89..37c4386 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -408,13 +408,15 @@ static void ioapic_machine_done_notify(Notifier *notifier, void *data) #endif } +#define IOAPIC_VER_DEF 0x20 + static void ioapic_realize(DeviceState *dev, Error **errp) { IOAPICCommonState *s = IOAPIC_COMMON(dev); if (s->version != 0x11 && s->version != 0x20) { error_report("IOAPIC only supports version 0x11 or 0x20 " - "(default: 0x11)."); + "(default: 0x%x).", IOAPIC_VER_DEF); exit(1); } @@ -429,7 +431,7 @@ static void ioapic_realize(DeviceState *dev, Error **errp) } static Property ioapic_properties[] = { - DEFINE_PROP_UINT8("version", IOAPICCommonState, version, 0x20), + DEFINE_PROP_UINT8("version", IOAPICCommonState, version, IOAPIC_VER_DEF), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index bad1d33..6563518 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -255,9 +255,9 @@ static void mcf5208evb_init(MachineState *machine) /* Internal peripherals. */ pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); - mcf_uart_mm_init(address_space_mem, 0xfc060000, pic[26], serial_hds[0]); - mcf_uart_mm_init(address_space_mem, 0xfc064000, pic[27], serial_hds[1]); - mcf_uart_mm_init(address_space_mem, 0xfc068000, pic[28], serial_hds[2]); + mcf_uart_mm_init(0xfc060000, pic[26], serial_hds[0]); + mcf_uart_mm_init(0xfc064000, pic[27], serial_hds[1]); + mcf_uart_mm_init(0xfc068000, pic[28], serial_hds[2]); mcf5208_sys_init(address_space_mem, pic); diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 0ac1e6a..57da7f2 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -42,7 +42,7 @@ static void handle_event(int event) } if (event & PVPANIC_PANICKED) { - qemu_system_guest_panicked(); + qemu_system_guest_panicked(NULL); return; } } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index bb19944..619f32c 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -334,7 +334,8 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu, { target_ulong ret = 0; - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, false, NULL, + &error_abort); rtas_st(rets, 0, ret); } diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index bbc9478..21ab7bf 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -318,6 +318,7 @@ static inline void tb_set_jmp_target(TranslationBlock *tb, static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb_next) { + assert(n < ARRAY_SIZE(tb->jmp_list_next)); if (tb->jmp_list_next[n]) { /* Another thread has already done this while we were * outside of the lock; nothing to do in this case */ diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h index 9a0bcfa..0db49c5 100644 --- a/include/hw/m68k/mcf.h +++ b/include/hw/m68k/mcf.h @@ -4,17 +4,13 @@ #include "target/m68k/cpu-qom.h" -struct MemoryRegion; - /* mcf_uart.c */ uint64_t mcf_uart_read(void *opaque, hwaddr addr, unsigned size); void mcf_uart_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); void *mcf_uart_init(qemu_irq irq, Chardev *chr); -void mcf_uart_mm_init(struct MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, Chardev *chr); +void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chr); /* mcf_intc.c */ qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 45bcf21..f69b240 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -158,6 +158,7 @@ typedef struct CPUClass { uint8_t *buf, int len, bool is_write); void (*dump_state)(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); + GuestPanicInformation* (*get_crash_info)(CPUState *cpu); void (*dump_statistics)(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); int64_t (*get_arch_id)(CPUState *cpu); @@ -472,6 +473,15 @@ int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, void *opaque); /** + * cpu_get_crash_info: + * @cpu: The CPU to get crash information for + * + * Gets the previously saved crash information. + * Caller is responsible for freeing the data. + */ +GuestPanicInformation *cpu_get_crash_info(CPUState *cpu); + +/** * CPUDumpFlags: * @CPU_DUMP_CODE: * @CPU_DUMP_FPU: dump FPU register state, not just integer diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 4d50694..576c7ce 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -37,6 +37,7 @@ void vm_state_notify(int running, RunState state); #define VMRESET_REPORT true void vm_start(void); +int vm_prepare_start(void); int vm_stop(RunState state); int vm_stop_force_state(RunState state); @@ -60,11 +61,12 @@ void qemu_register_powerdown_notifier(Notifier *notifier); void qemu_system_debug_request(void); void qemu_system_vmstop_request(RunState reason); void qemu_system_vmstop_request_prepare(void); +bool qemu_vmstop_requested(RunState *r); int qemu_shutdown_requested_get(void); int qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(bool report); -void qemu_system_guest_panicked(void); +void qemu_system_guest_panicked(GuestPanicInformation *info); size_t qemu_target_page_bits(void); void qemu_add_exit_notifier(Notifier *notify); @@ -2000,8 +2000,9 @@ int kvm_cpu_exec(CPUState *cpu) ret = EXCP_INTERRUPT; break; case KVM_SYSTEM_EVENT_CRASH: + kvm_cpu_synchronize_state(cpu); qemu_mutex_lock_iothread(); - qemu_system_guest_panicked(); + qemu_system_guest_panicked(cpu_get_crash_info(cpu)); qemu_mutex_unlock_iothread(); ret = 0; break; diff --git a/qapi-schema.json b/qapi-schema.json index 5edb08d..baa0d26 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5871,6 +5871,30 @@ 'data': [ 'pause', 'poweroff' ] } ## +# @GuestPanicInformation: +# +# Information about a guest panic +# +# Since: 2.9 +## +{'union': 'GuestPanicInformation', + 'data': { 'hyper-v': 'GuestPanicInformationHyperV' } } + +## +# @GuestPanicInformationHyperV: +# +# Hyper-V specific guest panic information (HV crash MSRs) +# +# Since: 2.9 +## +{'struct': 'GuestPanicInformationHyperV', + 'data': { 'arg1': 'uint64', + 'arg2': 'uint64', + 'arg3': 'uint64', + 'arg4': 'uint64', + 'arg5': 'uint64' } } + +## # @rtc-reset-reinjection: # # This command will reset the RTC interrupt reinjection backlog. diff --git a/qapi/event.json b/qapi/event.json index 7bf539b..970ff02 100644 --- a/qapi/event.json +++ b/qapi/event.json @@ -488,7 +488,9 @@ # # @action: action that has been taken, currently always "pause" # -# Since: 1.5 +# @info: optional information about a panic +# +# Since: 1.5 (@info since 2.9) # # Example: # @@ -497,7 +499,7 @@ # ## { 'event': 'GUEST_PANICKED', - 'data': { 'action': 'GuestPanicAction' } } + 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } ## # @QUORUM_FAILURE: @@ -463,6 +463,135 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) return creds; } +static void setup_address_and_port(const char **address, const char **port) +{ + if (*address == NULL) { + *address = "0.0.0.0"; + } + + if (*port == NULL) { + *port = stringify(NBD_DEFAULT_PORT); + } +} + +#define FIRST_SOCKET_ACTIVATION_FD 3 /* defined by systemd ABI */ + +#ifndef _WIN32 +/* + * Check if socket activation was requested via use of the + * LISTEN_FDS and LISTEN_PID environment variables. + * + * Returns 0 if no socket activation, or the number of FDs. + */ +static unsigned int check_socket_activation(void) +{ + const char *s; + unsigned long pid; + unsigned long nr_fds; + unsigned int i; + int fd; + int err; + + s = getenv("LISTEN_PID"); + if (s == NULL) { + return 0; + } + err = qemu_strtoul(s, NULL, 10, &pid); + if (err) { + if (verbose) { + fprintf(stderr, "malformed %s environment variable (ignored)\n", + "LISTEN_PID"); + } + return 0; + } + if (pid != getpid()) { + if (verbose) { + fprintf(stderr, "%s was not for us (ignored)\n", + "LISTEN_PID"); + } + return 0; + } + + s = getenv("LISTEN_FDS"); + if (s == NULL) { + return 0; + } + err = qemu_strtoul(s, NULL, 10, &nr_fds); + if (err) { + if (verbose) { + fprintf(stderr, "malformed %s environment variable (ignored)\n", + "LISTEN_FDS"); + } + return 0; + } + assert(nr_fds <= UINT_MAX); + + /* A limitation of current qemu-nbd is that it can only listen on + * a single socket. When that limitation is lifted, we can change + * this function to allow LISTEN_FDS > 1, and remove the assertion + * in the main function below. + */ + if (nr_fds > 1) { + error_report("qemu-nbd does not support socket activation with %s > 1", + "LISTEN_FDS"); + exit(EXIT_FAILURE); + } + + /* So these are not passed to any child processes we might start. */ + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_PID"); + + /* So the file descriptors don't leak into child processes. */ + for (i = 0; i < nr_fds; ++i) { + fd = FIRST_SOCKET_ACTIVATION_FD + i; + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + /* If we cannot set FD_CLOEXEC then it probably means the file + * descriptor is invalid, so socket activation has gone wrong + * and we should exit. + */ + error_report("Socket activation failed: " + "invalid file descriptor fd = %d: %m", + fd); + exit(EXIT_FAILURE); + } + } + + return (unsigned int) nr_fds; +} + +#else /* !_WIN32 */ +static unsigned int check_socket_activation(void) +{ + return 0; +} +#endif + +/* + * Check socket parameters compatibility when socket activation is used. + */ +static const char *socket_activation_validate_opts(const char *device, + const char *sockpath, + const char *address, + const char *port) +{ + if (device != NULL) { + return "NBD device can't be set when using socket activation"; + } + + if (sockpath != NULL) { + return "Unix socket can't be set when using socket activation"; + } + + if (address != NULL) { + return "The interface can't be set when using socket activation"; + } + + if (port != NULL) { + return "TCP port number can't be set when using socket activation"; + } + + return NULL; +} int main(int argc, char **argv) { @@ -471,7 +600,7 @@ int main(int argc, char **argv) off_t dev_offset = 0; uint16_t nbdflags = 0; bool disconnect = false; - const char *bindto = "0.0.0.0"; + const char *bindto = NULL; const char *port = NULL; char *sockpath = NULL; char *device = NULL; @@ -533,6 +662,7 @@ int main(int argc, char **argv) char *trace_file = NULL; bool fork_process = false; int old_stderr = -1; + unsigned socket_activation; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -751,6 +881,19 @@ int main(int argc, char **argv) trace_init_file(trace_file); qemu_set_log(LOG_TRACE); + socket_activation = check_socket_activation(); + if (socket_activation == 0) { + setup_address_and_port(&bindto, &port); + } else { + /* Using socket activation - check user didn't use -p etc. */ + const char *err_msg = socket_activation_validate_opts(device, sockpath, + bindto, port); + if (err_msg != NULL) { + error_report("%s", err_msg); + exit(EXIT_FAILURE); + } + } + if (tlscredsid) { if (sockpath) { error_report("TLS is only supported with IPv4/IPv6"); @@ -855,7 +998,25 @@ int main(int argc, char **argv) snprintf(sockpath, 128, SOCKET_PATH, basename(device)); } - saddr = nbd_build_socket_address(sockpath, bindto, port); + if (socket_activation == 0) { + server_ioc = qio_channel_socket_new(); + saddr = nbd_build_socket_address(sockpath, bindto, port); + if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) { + object_unref(OBJECT(server_ioc)); + error_report_err(local_err); + return 1; + } + } else { + /* See comment in check_socket_activation above. */ + assert(socket_activation == 1); + server_ioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD, + &local_err); + if (server_ioc == NULL) { + error_report("Failed to use socket activation: %s", + error_get_pretty(local_err)); + exit(EXIT_FAILURE); + } + } if (qemu_init_main_loop(&local_err)) { error_report_err(local_err); @@ -950,13 +1111,6 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - server_ioc = qio_channel_socket_new(); - if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) { - object_unref(OBJECT(server_ioc)); - error_report_err(local_err); - return 1; - } - if (device) { int ret; diff --git a/qemu-options.hx b/qemu-options.hx index ac036b4..5633d39 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1194,12 +1194,12 @@ Select type of VGA card to emulate. Valid values for @var{type} are Cirrus Logic GD5446 Video card. All Windows versions starting from Windows 95 should recognize and use this graphic card. For optimal performances, use 16 bit color depth in the guest and the host OS. -(This one is the default) +(This card was the default before QEMU 2.2) @item std Standard VGA card with Bochs VBE extensions. If your guest OS supports the VESA 2.0 VBE extensions (e.g. Windows XP) and if you want to use high resolution modes (>= 1280x1024x16) then you should use -this option. +this option. (This card is the default since QEMU 2.2) @item vmware VMWare SVGA-II compatible adapter. Use it if you have sufficiently recent XFree86/XOrg server or Windows guest with a driver for this @@ -218,6 +218,17 @@ static bool cpu_common_exec_interrupt(CPUState *cpu, int int_req) return false; } +GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + GuestPanicInformation *res = NULL; + + if (cc->get_crash_info) { + res = cc->get_crash_info(cpu); + } + return res; +} + void cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags) { diff --git a/target/i386/cpu.c b/target/i386/cpu.c index eb49980..fd7add2 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3495,6 +3495,53 @@ static void x86_cpu_register_feature_bit_props(X86CPU *cpu, x86_cpu_register_bit_prop(cpu, name, &cpu->env.features[w], bitnr); } +static GuestPanicInformation *x86_cpu_get_crash_info(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + GuestPanicInformation *panic_info = NULL; + + if (env->features[FEAT_HYPERV_EDX] & HV_X64_GUEST_CRASH_MSR_AVAILABLE) { + GuestPanicInformationHyperV *panic_info_hv = + g_malloc0(sizeof(GuestPanicInformationHyperV)); + panic_info = g_malloc0(sizeof(GuestPanicInformation)); + + panic_info->type = GUEST_PANIC_INFORMATION_KIND_HYPER_V; + panic_info->u.hyper_v.data = panic_info_hv; + + assert(HV_X64_MSR_CRASH_PARAMS >= 5); + panic_info_hv->arg1 = env->msr_hv_crash_params[0]; + panic_info_hv->arg2 = env->msr_hv_crash_params[1]; + panic_info_hv->arg3 = env->msr_hv_crash_params[2]; + panic_info_hv->arg4 = env->msr_hv_crash_params[3]; + panic_info_hv->arg5 = env->msr_hv_crash_params[4]; + } + + return panic_info; +} +static void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CPUState *cs = CPU(obj); + GuestPanicInformation *panic_info; + + if (!cs->crash_occurred) { + error_setg(errp, "No crash occured"); + return; + } + + panic_info = x86_cpu_get_crash_info(cs); + if (panic_info == NULL) { + error_setg(errp, "No crash information"); + return; + } + + visit_type_GuestPanicInformation(v, "crash-information", &panic_info, + errp); + qapi_free_GuestPanicInformation(panic_info); +} + static void x86_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -3530,6 +3577,9 @@ static void x86_cpu_initfn(Object *obj) x86_cpu_get_feature_words, NULL, NULL, (void *)cpu->filtered_features, NULL); + object_property_add(obj, "crash-information", "GuestPanicInformation", + x86_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL); + cpu->hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY; for (w = 0; w < FEATURE_WORDS; w++) { @@ -3684,6 +3734,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->do_interrupt = x86_cpu_do_interrupt; cc->cpu_exec_interrupt = x86_cpu_exec_interrupt; cc->dump_state = x86_cpu_dump_state; + cc->get_crash_info = x86_cpu_get_crash_info; cc->set_pc = x86_cpu_set_pc; cc->synchronize_from_tb = x86_cpu_synchronize_from_tb; cc->gdb_read_register = x86_cpu_gdb_read_register; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 4d788d5..8df124f 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1621,8 +1621,9 @@ void helper_lock_init(void); /* svm_helper.c */ void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, - uint64_t param); -void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1); + uint64_t param, uintptr_t retaddr); +void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, + uintptr_t retaddr); /* seg_helper.c */ void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw); diff --git a/target/i386/excp_helper.c b/target/i386/excp_helper.c index f0dc499..ee596c6 100644 --- a/target/i386/excp_helper.c +++ b/target/i386/excp_helper.c @@ -39,7 +39,8 @@ void helper_raise_exception(CPUX86State *env, int exception_index) * needed. It should only be called, if this is not an interrupt. * Returns the new exception number. */ -static int check_exception(CPUX86State *env, int intno, int *error_code) +static int check_exception(CPUX86State *env, int intno, int *error_code, + uintptr_t retaddr) { int first_contributory = env->old_exception == 0 || (env->old_exception >= 10 && @@ -53,7 +54,7 @@ static int check_exception(CPUX86State *env, int intno, int *error_code) #if !defined(CONFIG_USER_ONLY) if (env->old_exception == EXCP08_DBLE) { if (env->hflags & HF_SVMI_MASK) { - cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0); /* does not return */ + cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */ } qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); @@ -93,10 +94,10 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, if (!is_int) { cpu_svm_check_intercept_param(env, SVM_EXIT_EXCP_BASE + intno, - error_code); - intno = check_exception(env, intno, &error_code); + error_code, retaddr); + intno = check_exception(env, intno, &error_code, retaddr); } else { - cpu_svm_check_intercept_param(env, SVM_EXIT_SWINT, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_SWINT, 0, retaddr); } cs->exception_index = intno; diff --git a/target/i386/helper.h b/target/i386/helper.h index 4c1aaff..6fb8fb9 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -99,7 +99,6 @@ DEF_HELPER_2(inl, tl, env, i32) DEF_HELPER_FLAGS_4(bpt_io, TCG_CALL_NO_WG, void, env, i32, i32, tl) DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64) -DEF_HELPER_3(vmexit, void, env, i32, i64) DEF_HELPER_4(svm_check_io, void, env, i32, i32, i32) DEF_HELPER_3(vmrun, void, env, int, int) DEF_HELPER_1(vmmcall, void, env) diff --git a/target/i386/misc_helper.c b/target/i386/misc_helper.c index 5029efe..ca2ea09 100644 --- a/target/i386/misc_helper.c +++ b/target/i386/misc_helper.c @@ -101,7 +101,7 @@ void helper_cpuid(CPUX86State *env) { uint32_t eax, ebx, ecx, edx; - cpu_svm_check_intercept_param(env, SVM_EXIT_CPUID, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_CPUID, 0, GETPC()); cpu_x86_cpuid(env, (uint32_t)env->regs[R_EAX], (uint32_t)env->regs[R_ECX], &eax, &ebx, &ecx, &edx); @@ -125,7 +125,7 @@ target_ulong helper_read_crN(CPUX86State *env, int reg) { target_ulong val; - cpu_svm_check_intercept_param(env, SVM_EXIT_READ_CR0 + reg, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_READ_CR0 + reg, 0, GETPC()); switch (reg) { default: val = env->cr[reg]; @@ -143,7 +143,7 @@ target_ulong helper_read_crN(CPUX86State *env, int reg) void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) { - cpu_svm_check_intercept_param(env, SVM_EXIT_WRITE_CR0 + reg, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_WRITE_CR0 + reg, 0, GETPC()); switch (reg) { case 0: cpu_x86_update_cr0(env, t0); @@ -179,7 +179,7 @@ void helper_invlpg(CPUX86State *env, target_ulong addr) { X86CPU *cpu = x86_env_get_cpu(env); - cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPG, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPG, 0, GETPC()); tlb_flush_page(CPU(cpu), addr); } @@ -190,7 +190,7 @@ void helper_rdtsc(CPUX86State *env) if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { raise_exception_ra(env, EXCP0D_GPF, GETPC()); } - cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0, GETPC()); val = cpu_get_tsc(env) + env->tsc_offset; env->regs[R_EAX] = (uint32_t)(val); @@ -208,7 +208,7 @@ void helper_rdpmc(CPUX86State *env) if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { raise_exception_ra(env, EXCP0D_GPF, GETPC()); } - cpu_svm_check_intercept_param(env, SVM_EXIT_RDPMC, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_RDPMC, 0, GETPC()); /* currently unimplemented */ qemu_log_mask(LOG_UNIMP, "x86: unimplemented rdpmc\n"); @@ -228,7 +228,7 @@ void helper_wrmsr(CPUX86State *env) { uint64_t val; - cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1); + cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1, GETPC()); val = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); @@ -388,7 +388,7 @@ void helper_rdmsr(CPUX86State *env) { uint64_t val; - cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0, GETPC()); switch ((uint32_t)env->regs[R_ECX]) { case MSR_IA32_SYSENTER_CS: @@ -557,7 +557,7 @@ void helper_hlt(CPUX86State *env, int next_eip_addend) { X86CPU *cpu = x86_env_get_cpu(env); - cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0, GETPC()); env->eip += next_eip_addend; do_hlt(cpu); @@ -569,7 +569,7 @@ void helper_monitor(CPUX86State *env, target_ulong ptr) raise_exception_ra(env, EXCP0D_GPF, GETPC()); } /* XXX: store address? */ - cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0, GETPC()); } void helper_mwait(CPUX86State *env, int next_eip_addend) @@ -580,7 +580,7 @@ void helper_mwait(CPUX86State *env, int next_eip_addend) if ((uint32_t)env->regs[R_ECX] != 0) { raise_exception_ra(env, EXCP0D_GPF, GETPC()); } - cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0, GETPC()); env->eip += next_eip_addend; cpu = x86_env_get_cpu(env); @@ -597,7 +597,7 @@ void helper_pause(CPUX86State *env, int next_eip_addend) { X86CPU *cpu = x86_env_get_cpu(env); - cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0, GETPC()); env->eip += next_eip_addend; do_pause(cpu); diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c index d24574d..5c845dc 100644 --- a/target/i386/seg_helper.c +++ b/target/i386/seg_helper.c @@ -1335,7 +1335,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } else if (env->hflags2 & HF2_GIF_MASK) { if ((interrupt_request & CPU_INTERRUPT_SMI) && !(env->hflags & HF_SMM_MASK)) { - cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0); cs->interrupt_request &= ~CPU_INTERRUPT_SMI; do_smm_enter(cpu); ret = true; @@ -1356,7 +1356,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) (env->eflags & IF_MASK && !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { int intno; - cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0); cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); intno = cpu_get_pic_interrupt(env); @@ -1372,7 +1372,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) !(env->hflags & HF_INHIBIT_IRQ_MASK)) { int intno; /* FIXME: this should respect TPR */ - cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0); intno = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); qemu_log_mask(CPU_LOG_TB_IN_ASM, diff --git a/target/i386/svm_helper.c b/target/i386/svm_helper.c index 210f6aa..78d8df4 100644 --- a/target/i386/svm_helper.c +++ b/target/i386/svm_helper.c @@ -60,11 +60,8 @@ void helper_invlpga(CPUX86State *env, int aflag) { } -void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) -{ -} - -void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1) +void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, + uintptr_t retaddr) { } @@ -74,7 +71,7 @@ void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, } void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param) + uint64_t param, uintptr_t retaddr) { } @@ -130,7 +127,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) uint32_t event_inj; uint32_t int_ctl; - cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); if (aflag == 2) { addr = env->regs[R_EAX]; @@ -355,7 +352,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) void helper_vmmcall(CPUX86State *env) { - cpu_svm_check_intercept_param(env, SVM_EXIT_VMMCALL, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_VMMCALL, 0, GETPC()); raise_exception(env, EXCP06_ILLOP); } @@ -364,7 +361,7 @@ void helper_vmload(CPUX86State *env, int aflag) CPUState *cs = CPU(x86_env_get_cpu(env)); target_ulong addr; - cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); if (aflag == 2) { addr = env->regs[R_EAX]; @@ -404,7 +401,7 @@ void helper_vmsave(CPUX86State *env, int aflag) CPUState *cs = CPU(x86_env_get_cpu(env)); target_ulong addr; - cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); if (aflag == 2) { addr = env->regs[R_EAX]; @@ -445,19 +442,19 @@ void helper_vmsave(CPUX86State *env, int aflag) void helper_stgi(CPUX86State *env) { - cpu_svm_check_intercept_param(env, SVM_EXIT_STGI, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_STGI, 0, GETPC()); env->hflags2 |= HF2_GIF_MASK; } void helper_clgi(CPUX86State *env) { - cpu_svm_check_intercept_param(env, SVM_EXIT_CLGI, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_CLGI, 0, GETPC()); env->hflags2 &= ~HF2_GIF_MASK; } void helper_skinit(CPUX86State *env) { - cpu_svm_check_intercept_param(env, SVM_EXIT_SKINIT, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_SKINIT, 0, GETPC()); /* XXX: not implemented */ raise_exception(env, EXCP06_ILLOP); } @@ -467,7 +464,7 @@ void helper_invlpga(CPUX86State *env, int aflag) X86CPU *cpu = x86_env_get_cpu(env); target_ulong addr; - cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPGA, 0); + cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPGA, 0, GETPC()); if (aflag == 2) { addr = env->regs[R_EAX]; @@ -480,8 +477,8 @@ void helper_invlpga(CPUX86State *env, int aflag) tlb_flush_page(CPU(cpu), addr); } -void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param) +void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, + uint64_t param, uintptr_t retaddr) { CPUState *cs = CPU(x86_env_get_cpu(env)); @@ -491,27 +488,27 @@ void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, switch (type) { case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8: if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) { - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); } break; case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8: if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) { - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); } break; case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7: if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) { - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); } break; case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7: if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) { - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); } break; case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31: if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) { - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); } break; case SVM_EXIT_MSR: @@ -538,28 +535,28 @@ void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, t0 %= 8; break; default: - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); t0 = 0; t1 = 0; break; } if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); } } break; default: if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) { - helper_vmexit(env, type, param); + cpu_vmexit(env, type, param, retaddr); } break; } } -void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param) +void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, + uint64_t param) { - helper_svm_check_intercept_param(env, type, param); + cpu_svm_check_intercept_param(env, type, param, GETPC()); } void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, @@ -578,17 +575,22 @@ void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), env->eip + next_eip_addend); - helper_vmexit(env, SVM_EXIT_IOIO, param | (port << 16)); + cpu_vmexit(env, SVM_EXIT_IOIO, param | (port << 16), GETPC()); } } } /* Note: currently only 32 bits of exit_code are used */ -void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) +void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1, + uintptr_t retaddr) { CPUState *cs = CPU(x86_env_get_cpu(env)); uint32_t int_ctl; + if (retaddr) { + cpu_restore_state(cs, retaddr); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" PRIx64 ", " TARGET_FMT_lx ")!\n", exit_code, exit_info_1, @@ -766,9 +768,4 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) cpu_loop_exit(cs); } -void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) -{ - helper_vmexit(env, exit_code, exit_info_1); -} - #endif diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 6ed3876..2536780 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1864,7 +1864,7 @@ static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset) str, cs->cpu_index, ldq_phys(cs->as, cpu->env.psa + pswoffset), ldq_phys(cs->as, cpu->env.psa + pswoffset + 8)); s390_cpu_halt(cpu); - qemu_system_guest_panicked(); + qemu_system_guest_panicked(NULL); } static int handle_intercept(S390CPU *cpu) @@ -1897,7 +1897,7 @@ static int handle_intercept(S390CPU *cpu) if (is_special_wait_psw(cs)) { qemu_system_shutdown_request(); } else { - qemu_system_guest_panicked(); + qemu_system_guest_panicked(NULL); } } r = EXCP_HALTED; diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index d0dd390..39f338a 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -33,17 +33,6 @@ static char temp_file[] = "/tmp/vmst.test.XXXXXX"; static int temp_fd; -/* Fake yield_until_fd_readable() implementation so we don't have to pull the - * coroutine code as dependency. - */ -void yield_until_fd_readable(int fd) -{ - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - select(fd + 1, &fds, NULL, NULL, NULL); -} - /* Duplicate temp_fd and seek to the beginning of the file */ static QEMUFile *open_test_file(bool write) @@ -724,7 +724,7 @@ StatusInfo *qmp_query_status(Error **errp) return info; } -static bool qemu_vmstop_requested(RunState *r) +bool qemu_vmstop_requested(RunState *r) { qemu_mutex_lock(&vmstop_lock); *r = vmstop_requested; @@ -745,34 +745,6 @@ void qemu_system_vmstop_request(RunState state) qemu_notify_event(); } -void vm_start(void) -{ - RunState requested; - - qemu_vmstop_requested(&requested); - if (runstate_is_running() && requested == RUN_STATE__MAX) { - return; - } - - /* Ensure that a STOP/RESUME pair of events is emitted if a - * vmstop request was pending. The BLOCK_IO_ERROR event, for - * example, according to documentation is always followed by - * the STOP event. - */ - if (runstate_is_running()) { - qapi_event_send_stop(&error_abort); - } else { - replay_enable_events(); - cpu_enable_ticks(); - runstate_set(RUN_STATE_RUNNING); - vm_state_notify(1, RUN_STATE_RUNNING); - resume_all_vcpus(); - } - - qapi_event_send_resume(&error_abort); -} - - /***********************************************************/ /* real time host monotonic timer */ @@ -1707,18 +1679,34 @@ void qemu_system_reset(bool report) cpu_synchronize_all_post_reset(); } -void qemu_system_guest_panicked(void) +void qemu_system_guest_panicked(GuestPanicInformation *info) { + qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed\n"); + if (current_cpu) { current_cpu->crash_occurred = true; } - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, + !!info, info, &error_abort); vm_stop(RUN_STATE_GUEST_PANICKED); if (!no_shutdown) { qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_POWEROFF, - &error_abort); + !!info, info, &error_abort); qemu_system_shutdown_request(); } + + if (info) { + if (info->type == GUEST_PANIC_INFORMATION_KIND_HYPER_V) { + qemu_log_mask(LOG_GUEST_ERROR, "HV crash parameters: (%#"PRIx64 + " %#"PRIx64" %#"PRIx64" %#"PRIx64" %#"PRIx64")\n", + info->u.hyper_v.data->arg1, + info->u.hyper_v.data->arg2, + info->u.hyper_v.data->arg3, + info->u.hyper_v.data->arg4, + info->u.hyper_v.data->arg5); + } + qapi_free_GuestPanicInformation(info); + } } void qemu_system_reset_request(void) @@ -4490,8 +4478,6 @@ int main(int argc, char **argv, char **envp) audio_init(); - cpu_synchronize_all_post_init(); - if (hax_enabled()) { hax_sync_vcpus(); } @@ -4517,6 +4503,8 @@ int main(int argc, char **argv, char **envp) exit(1); } + cpu_synchronize_all_post_init(); + numa_post_machine_init(); rom_reset_order_override(); |