diff --git a/kvm-all.c b/kvm-all.c
index 9b73ccf..b5b0531 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -19,6 +19,7 @@
 #include <stdarg.h>
 
 #include <linux/kvm.h>
+#include <linux/kvm_para.h>
 
 #include "qemu-common.h"
 #include "qemu-barrier.h"
@@ -29,6 +30,8 @@
 #include "bswap.h"
 #include "memory.h"
 #include "exec-memory.h"
+#include "iorange.h"
+#include "qemu-objects.h"
 
 /* This check must be after config-host.h is included */
 #ifdef CONFIG_EVENTFD
@@ -1707,3 +1710,84 @@ int kvm_on_sigbus(int code, void *addr)
 {
     return kvm_arch_on_sigbus(code, addr);
 }
+
+/* Possible values for action parameter. */
+#define PANICKED_REPORT 1   /* emit QEVENT_GUEST_PANICKED only */
+#define PANICKED_PAUSE  2   /* emit QEVENT_GUEST_PANICKED and pause VM */
+#define PANICKED_QUIT   3   /* emit QEVENT_GUEST_PANICKED and quit VM */
+
+static int panicked_action = PANICKED_REPORT;
+
+static void kvm_pv_port_read(IORange *iorange, uint64_t offset, unsigned width,
+                             uint64_t *data)
+{
+    *data = (1 << KVM_PV_FEATURE_PANICKED);
+}
+
+static void panicked_mon_event(const char *action)
+{
+    QObject *data;
+
+    data = qobject_from_jsonf("{ 'action': %s }", action);
+    monitor_protocol_event(QEVENT_GUEST_PANICKED, data);
+    qobject_decref(data);
+}
+
+static void panicked_perform_action(void)
+{
+    switch(panicked_action) {
+    case PANICKED_REPORT:
+        panicked_mon_event("report");
+        break;
+
+    case PANICKED_PAUSE:
+        panicked_mon_event("pause");
+        vm_stop(RUN_STATE_GUEST_PANICKED);
+        break;
+
+    case PANICKED_QUIT:
+        panicked_mon_event("quit");
+        exit(0);
+        break;
+    }
+}
+
+static void kvm_pv_port_write(IORange *iorange, uint64_t offset, unsigned width,
+                              uint64_t data)
+{
+    if (data == KVM_PV_PANICKED)
+        panicked_perform_action();
+}
+
+static void kvm_pv_port_destructor(IORange *iorange)
+{
+    g_free(iorange);
+}
+
+static IORangeOps pv_io_range_ops = {
+    .read = kvm_pv_port_read,
+    .write = kvm_pv_port_write,
+    .destructor = kvm_pv_port_destructor,
+};
+
+void kvm_pv_port_init(void)
+{
+    IORange *pv_io_range = g_malloc(sizeof(IORange));
+
+    iorange_init(pv_io_range, &pv_io_range_ops, 0x505, 1);
+    ioport_register(pv_io_range);
+}
+
+int select_panicked_action(const char *p)
+{
+    if (strcasecmp(p, "report") == 0)
+        panicked_action = PANICKED_REPORT;
+    else if (strcasecmp(p, "pause") == 0)
+        panicked_action = PANICKED_PAUSE;
+    else if (strcasecmp(p, "quit") == 0)
+        panicked_action = PANICKED_QUIT;
+    else
+        return -1;
+
+    return 0;
+}
diff --git a/kvm-stub.c b/kvm-stub.c
index 47c573d..4cf977e 100644
--- a/kvm-stub.c
+++ b/kvm-stub.c
@@ -128,3 +128,12 @@ int kvm_on_sigbus(int code, void *addr)
 {
     return 1;
 }
+
+void kvm_pv_port_init(void)
+{
+}
+
+int select_panicked_action(const char *p)
+{
+    return -1;
+}
diff --git a/kvm.h b/kvm.h
index 4ccae8c..95075cf 100644
--- a/kvm.h
+++ b/kvm.h
@@ -60,6 +60,9 @@ int kvm_has_gsi_routing(void);
 
 int kvm_allows_irq0_override(void);
 
+void kvm_pv_port_init(void);
+int select_panicked_action(const char *p);
+
 #ifdef NEED_CPU_H
 int kvm_init_vcpu(CPUArchState *env);
 
diff --git a/monitor.c b/monitor.c
index 12a6fe2..83cb059 100644
--- a/monitor.c
+++ b/monitor.c
@@ -493,6 +493,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
         case QEVENT_WAKEUP:
             event_name = "WAKEUP";
             break;
+        case QEVENT_GUEST_PANICKED:
+            event_name = "GUEST_PANICKED";
+            break;
         default:
             abort();
             break;
diff --git a/monitor.h b/monitor.h
index 0d49800..94e8a3c 100644
--- a/monitor.h
+++ b/monitor.h
@@ -41,6 +41,7 @@ typedef enum MonitorEvent {
     QEVENT_DEVICE_TRAY_MOVED,
     QEVENT_SUSPEND,
     QEVENT_WAKEUP,
+    QEVENT_GUEST_PANICKED,
     QEVENT_MAX,
 } MonitorEvent;
 
diff --git a/qapi-schema.json b/qapi-schema.json
index 2ca7195..ee5c9e9 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -119,11 +119,15 @@
 # @suspended: guest is suspended (ACPI S3)
 #
 # @watchdog: the watchdog action is configured to pause and has been triggered
+#
+# @guest-panicked: the panicked action is configured to pause and has been
+# triggered.
 ##
 { 'enum': 'RunState',
   'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused',
             'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm',
-            'running', 'save-vm', 'shutdown', 'suspended', 'watchdog' ] }
+            'running', 'save-vm', 'shutdown', 'suspended', 'watchdog',
+            'guest-panicked' ] }
 
 ##
 # @StatusInfo:
diff --git a/qemu-options.hx b/qemu-options.hx
index 8b66264..d3d21a1 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2743,6 +2743,20 @@ DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log,
     "-qtest-log LOG  specify tracing options\n",
     QEMU_ARCH_ALL)
 
+DEF("onpanic", HAS_ARG, QEMU_OPTION_onpanic, \
+    "-onpanic report|pause|quit\n" \
+    "                action when the guest is panicked [default=report]",
+    QEMU_ARCH_ALL)
+STEXI
+@item -onpanic @var{action}
+
+The @var{action} controls what QEmu will do when the guest is panicked.
+The default is @code{report} (emit QEVENT_GUEST_PANICKED only).
+Other possible actions are:
+@code{pause} (emit QEVENT_GUEST_PANICKED and pause VM),
+@code{quit} (emit QEVENT_GUEST_PANICKED and quit VM).
+ETEXI
+
 HXCOMM This is the last statement. Insert new options before this line!
 STEXI
 @end table
diff --git a/qmp.c b/qmp.c
index fee9fb2..a2d5ce9 100644
--- a/qmp.c
+++ b/qmp.c
@@ -148,7 +148,8 @@ void qmp_cont(Error **errp)
         error_set(errp, QERR_MIGRATION_EXPECTED);
         return;
     } else if (runstate_check(RUN_STATE_INTERNAL_ERROR) ||
-               runstate_check(RUN_STATE_SHUTDOWN)) {
+               runstate_check(RUN_STATE_SHUTDOWN) ||
+               runstate_check(RUN_STATE_GUEST_PANICKED)) {
         error_set(errp, QERR_RESET_REQUIRED);
         return;
     } else if (runstate_check(RUN_STATE_SUSPENDED)) {
diff --git a/vl.c b/vl.c
index 7f5fed8..8848506 100644
--- a/vl.c
+++ b/vl.c
@@ -367,6 +367,7 @@ static const RunStateTransition runstate_transitions_def[] = {
     { RUN_STATE_RUNNING, RUN_STATE_SAVE_VM },
     { RUN_STATE_RUNNING, RUN_STATE_SHUTDOWN },
     { RUN_STATE_RUNNING, RUN_STATE_WATCHDOG },
+    { RUN_STATE_RUNNING, RUN_STATE_GUEST_PANICKED },
 
     { RUN_STATE_SAVE_VM, RUN_STATE_RUNNING },
 
@@ -381,6 +382,9 @@ static const RunStateTransition runstate_transitions_def[] = {
     { RUN_STATE_WATCHDOG, RUN_STATE_RUNNING },
     { RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE },
 
+    { RUN_STATE_GUEST_PANICKED, RUN_STATE_PAUSED },
+    { RUN_STATE_GUEST_PANICKED, RUN_STATE_FINISH_MIGRATE },
+
     { RUN_STATE_MAX, RUN_STATE_MAX },
 };
 
@@ -1537,7 +1541,8 @@ static bool main_loop_should_exit(void)
         qemu_system_reset(VMRESET_REPORT);
         resume_all_vcpus();
         if (runstate_check(RUN_STATE_INTERNAL_ERROR) ||
-            runstate_check(RUN_STATE_SHUTDOWN)) {
+            runstate_check(RUN_STATE_SHUTDOWN) ||
+            runstate_check(RUN_STATE_GUEST_PANICKED)) {
             runstate_set(RUN_STATE_PAUSED);
             vm_start();
         }
@@ -3202,6 +3207,12 @@ int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_qtest_log:
                 qtest_log = optarg;
                 break;
+            case QEMU_OPTION_onpanic:
+                if (select_panicked_action(optarg) == -1) {
+                    fprintf(stderr, "Unknown -onpanic parameter\n");
+                    exit(1);
+                }
+                break;
             default:
                 os_parse_cmd_args(popt->index, optarg);
             }
@@ -3634,6 +3645,10 @@ int main(int argc, char **argv, char **envp)
         }
     }
 
+    if (kvm_enabled()) {
+        kvm_pv_port_init();
+    }
+
     if (incoming) {
         Error *errp = NULL;
         int ret = qemu_start_incoming_migration(incoming, &errp);
