As discussed at length already, one potential 'workaround' for KVM brokenness in old kernels (<3.9) with old CPUs (without 'unrestricted guest' support), is to properly reset the PAM registers in the chipset.
I say 'workaround' but this would be a proper fix in its own right, and as a side-effect would help to work around the problem we're actually suffering. To do it properly, we need to distinguish between a 'hard' reset triggered by the PIIX3 RCR register, which resets the PAM configuration, and a 'soft' reset triggered for example by the keyboard controller, which doesn't. This patch attempts to introduce a ResetType into the code logic. Rather than propagating that ResetType through the entire stack of device reset functions, I've added a 'qemu_last_reset_get()' function so that the devices which *do* care can look for it. Comments? There are a whole bunch more qemu_system_reset_request() calls which will need a ResetType added, if I do it this way... diff --git a/hw/apb_pci.c b/hw/apb_pci.c index 7eb0c2b..79d7466 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -135,9 +135,9 @@ static void apb_config_writel (void *opaque, hwaddr addr, s->reset_control |= val & RESET_WMASK; if (val & SOFT_POR) { s->nr_resets = 0; - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_SOFT); } else if (val & SOFT_XIR) { - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_SOFT); } } break; diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index 7ecb7da..b553539 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -239,7 +239,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, if (s->lockval == LOCK_VALUE) { s->resetlevel = val; if (val & 0x100) { - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_HARD); } } break; @@ -248,7 +248,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, if (s->lockval == LOCK_VALUE) { s->resetlevel = val; if (val & 0x04) { - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_HARD); } } break; diff --git a/hw/cuda.c b/hw/cuda.c index b36c535..20c0894 100644 --- a/hw/cuda.c +++ b/hw/cuda.c @@ -529,7 +529,7 @@ static void cuda_receive_packet(CUDAState *s, obuf[0] = CUDA_PACKET; obuf[1] = 0; cuda_send_packet_to_host(s, obuf, 2); - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_HARD); break; default: break; diff --git a/hw/m48t59.c b/hw/m48t59.c index 427d95b..5a21701 100644 --- a/hw/m48t59.c +++ b/hw/m48t59.c @@ -166,7 +166,7 @@ static void watchdog_cb (void *opaque) NVRAM->buffer[0x1FF7] = 0x00; NVRAM->buffer[0x1FFC] &= ~0x40; /* May it be a hw CPU Reset instead ? */ - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_HARD); } else { qemu_set_irq(NVRAM->IRQ, 1); qemu_set_irq(NVRAM->IRQ, 0); diff --git a/hw/pc.c b/hw/pc.c index 53cc173..d138a92 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -440,7 +440,7 @@ static void port92_write(void *opaque, hwaddr addr, uint64_t val, s->outport = val; qemu_set_irq(*s->a20_out, (val >> 1) & 1); if (val & 1) { - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_SOFT); } } diff --git a/hw/pckbd.c b/hw/pckbd.c index 3bad09b..b55d61e 100644 --- a/hw/pckbd.c +++ b/hw/pckbd.c @@ -220,7 +220,8 @@ static void outport_write(KBDState *s, uint32_t val) qemu_set_irq(*s->a20_out, (val >> 1) & 1); } if (!(val & 1)) { - qemu_system_reset_request(); + /* This should be a soft reset, not full chipset reset. */ + qemu_system_reset_request(QEMU_RESET_SOFT); } } @@ -299,7 +300,7 @@ static void kbd_write_command(void *opaque, hwaddr addr, s->outport &= ~KBD_OUT_A20; break; case KBD_CCMD_RESET: - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_SOFT); break; case KBD_CCMD_NO_OP: /* ignore that */ diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 6c77e49..55654c0 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -171,6 +171,27 @@ static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) return 0; } +static void i440fx_reset(DeviceState *ds) +{ + PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, ds); + PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev); + uint8_t *pci_conf = d->dev.config; + + if (qemu_last_reset_get() < QEMU_RESET_HARD) + return; + + pci_conf[0x59] = 0x00; // Reset PAM setup + pci_conf[0x5a] = 0x00; + pci_conf[0x5b] = 0x00; + pci_conf[0x5c] = 0x00; + pci_conf[0x5d] = 0x00; + pci_conf[0x5e] = 0x00; + pci_conf[0x5f] = 0x00; + pci_conf[0x72] = 0x02; // And SMM + + i440fx_update_memory_mappings(d); +} + static int i440fx_post_load(void *opaque, int version_id) { PCII440FXState *d = opaque; @@ -521,7 +542,10 @@ static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) PIIX3State *d = opaque; if (val & 4) { - qemu_system_reset_request(); + if (val & 2) + qemu_system_reset_request(QEMU_RESET_HARD); + else + qemu_system_reset_request(QEMU_RESET_SOFT); return; } d->rcr = val & 2; /* keep System Reset type only */ @@ -615,6 +639,7 @@ static void i440fx_class_init(ObjectClass *klass, void *data) dc->desc = "Host bridge"; dc->no_user = 1; dc->vmsd = &vmstate_i440fx; + dc->reset = i440fx_reset; } static const TypeInfo i440fx_info = { diff --git a/hw/watchdog.c b/hw/watchdog.c index 072d256..ba5aa7e 100644 --- a/hw/watchdog.c +++ b/hw/watchdog.c @@ -117,7 +117,7 @@ void watchdog_perform_action(void) switch(watchdog_action) { case WDT_RESET: /* same as 'system_reset' in monitor */ watchdog_mon_event("reset"); - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_HARD); break; case WDT_SHUTDOWN: /* same as 'system_powerdown' in monitor */ diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 1d9599e..90635a5 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -33,6 +33,14 @@ void vm_state_notify(int running, RunState state); #define VMRESET_SILENT false #define VMRESET_REPORT true +typedef enum ResetType { + QEMU_RESET_NONE = 0, + QEMU_RESET_WAKEUP, + QEMU_RESET_SOFT, + QEMU_RESET_HARD, + QEMU_RESET_POWERON, +} ResetType; + void vm_start(void); void vm_stop(RunState state); void vm_stop_force_state(RunState state); @@ -43,7 +51,7 @@ typedef enum WakeupReason { QEMU_WAKEUP_REASON_PMTIMER, } WakeupReason; -void qemu_system_reset_request(void); +void qemu_system_reset_request(ResetType type); void qemu_system_suspend_request(void); void qemu_register_suspend_notifier(Notifier *notifier); void qemu_system_wakeup_request(WakeupReason reason); @@ -55,10 +63,11 @@ void qemu_register_powerdown_notifier(Notifier *notifier); void qemu_system_debug_request(void); void qemu_system_vmstop_request(RunState reason); int qemu_shutdown_requested_get(void); -int qemu_reset_requested_get(void); +ResetType qemu_reset_requested_get(void); +ResetType qemu_last_reset_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_devices_reset(void); -void qemu_system_reset(bool report); +void qemu_system_reset(ResetType type, bool report); void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); diff --git a/kvm-all.c b/kvm-all.c index 04ec2d5..1177ca1 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -1599,7 +1599,7 @@ int kvm_cpu_exec(CPUArchState *env) break; case KVM_EXIT_SHUTDOWN: DPRINTF("shutdown\n"); - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_HARD); ret = EXCP_INTERRUPT; break; case KVM_EXIT_UNKNOWN: diff --git a/qmp.c b/qmp.c index 55b056b..34c0429 100644 --- a/qmp.c +++ b/qmp.c @@ -95,7 +95,7 @@ void qmp_stop(Error **errp) void qmp_system_reset(Error **errp) { - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_HARD); } void qmp_system_powerdown(Error **erp) diff --git a/qtest.c b/qtest.c index 4663a38..61190f4 100644 --- a/qtest.c +++ b/qtest.c @@ -391,7 +391,7 @@ static void qtest_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: - qemu_system_reset(false); + qemu_system_reset(QEMU_RESET_POWERON, false); for (i = 0; i < ARRAY_SIZE(irq_levels); i++) { irq_levels[i] = 0; } diff --git a/savevm.c b/savevm.c index a8a53ef..035901b 100644 --- a/savevm.c +++ b/savevm.c @@ -2299,7 +2299,7 @@ int load_vmstate(const char *name) return -EINVAL; } - qemu_system_reset(VMRESET_SILENT); + qemu_system_reset(QEMU_RESET_WAKEUP, VMRESET_SILENT); ret = qemu_loadvm_state(f); qemu_fclose(f); diff --git a/target-i386/excp_helper.c b/target-i386/excp_helper.c index 179ea82..2faf53f 100644 --- a/target-i386/excp_helper.c +++ b/target-i386/excp_helper.c @@ -64,7 +64,7 @@ static int check_exception(CPUX86State *env, int intno, int *error_code) qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_SOFT); return EXCP_HLT; } #endif diff --git a/target-i386/helper.c b/target-i386/helper.c index d1cb4e2..cc0de0e 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -1147,7 +1147,7 @@ static void do_inject_x86_mce(void *data) " triple fault\n", cpu->cpu_index); qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_SOFT); return; } if (banks[1] & MCI_STATUS_VAL) { diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 9ebf181..f52a3d6 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -1846,7 +1846,7 @@ int kvm_arch_process_async_events(CPUState *cs) if (env->exception_injected == EXCP08_DBLE) { /* this means triple fault */ - qemu_system_reset_request(); + qemu_system_reset_request(QEMU_RESET_SOFT); env->exit_request = 1; return 0; } diff --git a/vl.c b/vl.c index c5b0eea..948eb99 100644 --- a/vl.c +++ b/vl.c @@ -1696,7 +1696,8 @@ typedef struct QEMUResetEntry { static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers = QTAILQ_HEAD_INITIALIZER(reset_handlers); -static int reset_requested; +static ResetType reset_requested; +static ResetType last_reset = QEMU_RESET_POWERON; static int shutdown_requested, shutdown_signal = -1; static pid_t shutdown_pid; static int powerdown_requested; @@ -1717,11 +1718,16 @@ int qemu_shutdown_requested_get(void) return shutdown_requested; } -int qemu_reset_requested_get(void) +ResetType qemu_reset_requested_get(void) { return reset_requested; } +ResetType qemu_last_reset_get(void) +{ + return last_reset; +} + static int qemu_shutdown_requested(void) { int r = shutdown_requested; @@ -1745,10 +1751,10 @@ static void qemu_kill_report(void) } } -static int qemu_reset_requested(void) +static ResetType qemu_reset_requested(void) { - int r = reset_requested; - reset_requested = 0; + ResetType r = reset_requested; + reset_requested = QEMU_RESET_NONE; return r; } @@ -1824,8 +1830,9 @@ void qemu_devices_reset(void) } } -void qemu_system_reset(bool report) +void qemu_system_reset(ResetType type, bool report) { + last_reset = type; if (current_machine && current_machine->reset) { current_machine->reset(); } else { @@ -1837,12 +1844,12 @@ void qemu_system_reset(bool report) cpu_synchronize_all_post_reset(); } -void qemu_system_reset_request(void) +void qemu_system_reset_request(ResetType type) { if (no_reboot) { shutdown_requested = 1; } else { - reset_requested = 1; + reset_requested = type; } cpu_stop_current(); qemu_notify_event(); @@ -1945,6 +1952,7 @@ void qemu_system_vmstop_request(RunState state) static bool main_loop_should_exit(void) { RunState r; + ResetType rt; if (qemu_debug_requested()) { vm_stop(RUN_STATE_DEBUG); } @@ -1960,10 +1968,11 @@ static bool main_loop_should_exit(void) return true; } } - if (qemu_reset_requested()) { + rt = qemu_reset_requested(); + if (rt) { pause_all_vcpus(); cpu_synchronize_all_states(); - qemu_system_reset(VMRESET_REPORT); + qemu_system_reset(rt, VMRESET_REPORT); resume_all_vcpus(); if (runstate_check(RUN_STATE_INTERNAL_ERROR) || runstate_check(RUN_STATE_SHUTDOWN)) { @@ -1973,7 +1982,7 @@ static bool main_loop_should_exit(void) if (qemu_wakeup_requested()) { pause_all_vcpus(); cpu_synchronize_all_states(); - qemu_system_reset(VMRESET_SILENT); + qemu_system_reset(QEMU_RESET_WAKEUP, VMRESET_SILENT); resume_all_vcpus(); monitor_protocol_event(QEVENT_WAKEUP, NULL); } @@ -4308,7 +4317,7 @@ int main(int argc, char **argv, char **envp) qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); qemu_run_machine_init_done_notifiers(); - qemu_system_reset(VMRESET_SILENT); + qemu_system_reset(QEMU_RESET_POWERON, VMRESET_SILENT); if (loadvm) { if (load_vmstate(loadvm) < 0) { autostart = 0; diff --git a/xen-all.c b/xen-all.c index 110f958..4be465e 100644 --- a/xen-all.c +++ b/xen-all.c @@ -883,11 +883,13 @@ static void cpu_handle_ioreq(void *opaque) * causes Xen to powerdown the domain. */ if (runstate_is_running()) { + ResetType rt; if (qemu_shutdown_requested_get()) { destroy_hvm_domain(false); } - if (qemu_reset_requested_get()) { - qemu_system_reset(VMRESET_REPORT); + rt = qemu_reset_requested_get(); + if (rt) { + qemu_system_reset(rt, VMRESET_REPORT); destroy_hvm_domain(true); } } -- David Woodhouse Open Source Technology Centre david.woodho...@intel.com Intel Corporation
smime.p7s
Description: S/MIME cryptographic signature