xref: /openbmc/qemu/hw/misc/pvpanic.c (revision b42ffe60d8b510cd2f76ef50f6a1057f91a3dd34)
1 /*
2  * QEMU simulated pvpanic device.
3  *
4  * Copyright Fujitsu, Corp. 2013
5  *
6  * Authors:
7  *     Wen Congyang <wency@cn.fujitsu.com>
8  *     Hu Tao <hutao@cn.fujitsu.com>
9  *
10  * This work is licensed under the terms of the GNU GPL, version 2 or later.
11  * See the COPYING file in the top-level directory.
12  *
13  */
14 
15 #include "qapi/qmp/qobject.h"
16 #include "qapi/qmp/qjson.h"
17 #include "monitor/monitor.h"
18 #include "sysemu/sysemu.h"
19 #include "qemu/log.h"
20 
21 #include "hw/nvram/fw_cfg.h"
22 
23 /* The bit of supported pv event */
24 #define PVPANIC_F_PANICKED      0
25 
26 /* The pv event value */
27 #define PVPANIC_PANICKED        (1 << PVPANIC_F_PANICKED)
28 
29 #define TYPE_ISA_PVPANIC_DEVICE    "pvpanic"
30 #define ISA_PVPANIC_DEVICE(obj)    \
31     OBJECT_CHECK(PVPanicState, (obj), TYPE_ISA_PVPANIC_DEVICE)
32 
33 static void panicked_mon_event(const char *action)
34 {
35     QObject *data;
36 
37     data = qobject_from_jsonf("{ 'action': %s }", action);
38     monitor_protocol_event(QEVENT_GUEST_PANICKED, data);
39     qobject_decref(data);
40 }
41 
42 static void handle_event(int event)
43 {
44     static bool logged;
45 
46     if (event & ~PVPANIC_PANICKED && !logged) {
47         qemu_log_mask(LOG_GUEST_ERROR, "pvpanic: unknown event %#x.\n", event);
48         logged = true;
49     }
50 
51     if (event & PVPANIC_PANICKED) {
52         panicked_mon_event("pause");
53         vm_stop(RUN_STATE_GUEST_PANICKED);
54         return;
55     }
56 }
57 
58 #include "hw/isa/isa.h"
59 
60 typedef struct PVPanicState {
61     ISADevice parent_obj;
62 
63     MemoryRegion io;
64     uint16_t ioport;
65 } PVPanicState;
66 
67 /* return supported events on read */
68 static uint64_t pvpanic_ioport_read(void *opaque, hwaddr addr, unsigned size)
69 {
70     return PVPANIC_PANICKED;
71 }
72 
73 static void pvpanic_ioport_write(void *opaque, hwaddr addr, uint64_t val,
74                                  unsigned size)
75 {
76     handle_event(val);
77 }
78 
79 static const MemoryRegionOps pvpanic_ops = {
80     .read = pvpanic_ioport_read,
81     .write = pvpanic_ioport_write,
82     .impl = {
83         .min_access_size = 1,
84         .max_access_size = 1,
85     },
86 };
87 
88 static int pvpanic_isa_initfn(ISADevice *dev)
89 {
90     PVPanicState *s = ISA_PVPANIC_DEVICE(dev);
91     static bool port_configured;
92     void *fw_cfg;
93 
94     memory_region_init_io(&s->io, &pvpanic_ops, s, "pvpanic", 1);
95     isa_register_ioport(dev, &s->io, s->ioport);
96 
97     if (!port_configured) {
98         fw_cfg = object_resolve_path("/machine/fw_cfg", NULL);
99         if (fw_cfg) {
100             fw_cfg_add_file(fw_cfg, "etc/pvpanic-port",
101                             g_memdup(&s->ioport, sizeof(s->ioport)),
102                             sizeof(s->ioport));
103             port_configured = true;
104         }
105     }
106 
107     return 0;
108 }
109 
110 static Property pvpanic_isa_properties[] = {
111     DEFINE_PROP_UINT16("ioport", PVPanicState, ioport, 0x505),
112     DEFINE_PROP_END_OF_LIST(),
113 };
114 
115 static void pvpanic_isa_class_init(ObjectClass *klass, void *data)
116 {
117     DeviceClass *dc = DEVICE_CLASS(klass);
118     ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
119 
120     ic->init = pvpanic_isa_initfn;
121     dc->no_user = 1;
122     dc->props = pvpanic_isa_properties;
123 }
124 
125 static TypeInfo pvpanic_isa_info = {
126     .name          = TYPE_ISA_PVPANIC_DEVICE,
127     .parent        = TYPE_ISA_DEVICE,
128     .instance_size = sizeof(PVPanicState),
129     .class_init    = pvpanic_isa_class_init,
130 };
131 
132 static void pvpanic_register_types(void)
133 {
134     type_register_static(&pvpanic_isa_info);
135 }
136 
137 type_init(pvpanic_register_types)
138