2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 #include "qemu/osdep.h"
17 #include "qapi/error.h"
18 #include "target/ppc/cpu.h"
19 #include "migration/vmstate.h"
22 #include "hw/ppc/spapr.h"
24 #define FIELD_BE(reg, field, start, len) \
25 FIELD(reg, field, 64 - (start + len), len)
28 * Bits 47: "leaveOtherWatchdogsRunningOnTimeout", specified on
29 * the "Start watchdog" operation,
30 * 0 - stop out-standing watchdogs on timeout,
31 * 1 - leave outstanding watchdogs running on timeout
33 FIELD_BE(PSERIES_WDTF
, LEAVE_OTHER
, 47, 1)
35 /* Bits 48-55: "operation" */
36 FIELD_BE(PSERIES_WDTF
, OP
, 48, 8)
37 #define PSERIES_WDTF_OP_START 0x1
38 #define PSERIES_WDTF_OP_STOP 0x2
39 #define PSERIES_WDTF_OP_QUERY 0x3
40 #define PSERIES_WDTF_OP_QUERY_LPM 0x4
42 /* Bits 56-63: "timeoutAction" */
43 FIELD_BE(PSERIES_WDTF
, ACTION
, 56, 8)
44 #define PSERIES_WDTF_ACTION_HARD_POWER_OFF 0x1
45 #define PSERIES_WDTF_ACTION_HARD_RESTART 0x2
46 #define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3
48 FIELD_BE(PSERIES_WDTF
, RESERVED
, 0, 47)
50 /* Special watchdogNumber for the "stop all watchdogs" operation */
51 #define PSERIES_WDT_STOP_ALL ((uint64_t)~0)
54 * For the "Query watchdog capabilities" operation, a uint64 structure
56 * Bits 0-15: The minimum supported timeout in milliseconds
57 * Bits 16-31: The number of watchdogs supported
58 * Bits 32-63: Reserved
60 FIELD_BE(PSERIES_WDTQ
, MIN_TIMEOUT
, 0, 16)
61 FIELD_BE(PSERIES_WDTQ
, NUM
, 16, 16)
64 * For the "Query watchdog LPM requirement" operation:
65 * 1 = The given "watchdogNumber" must be stopped prior to suspending
66 * 2 = The given "watchdogNumber" does not have to be stopped prior to
69 #define PSERIES_WDTQL_STOPPED 1
70 #define PSERIES_WDTQL_QUERY_NOT_STOPPED 2
72 #define WDT_MIN_TIMEOUT 1 /* 1ms */
74 static target_ulong
watchdog_stop(unsigned watchdogNumber
, SpaprWatchdog
*w
)
76 target_ulong ret
= H_NOOP
;
78 if (timer_pending(&w
->timer
)) {
82 trace_spapr_watchdog_stop(watchdogNumber
, ret
);
87 static target_ulong
watchdog_stop_all(SpaprMachineState
*spapr
)
89 target_ulong ret
= H_NOOP
;
92 for (i
= 1; i
<= ARRAY_SIZE(spapr
->wds
); ++i
) {
93 target_ulong r
= watchdog_stop(i
, &spapr
->wds
[i
- 1]);
95 if (r
!= H_NOOP
&& r
!= H_SUCCESS
) {
103 static void watchdog_expired(void *pw
)
105 SpaprWatchdog
*w
= pw
;
107 SpaprMachineState
*spapr
= SPAPR_MACHINE(qdev_get_machine());
108 unsigned num
= w
- spapr
->wds
;
110 g_assert(num
< ARRAY_SIZE(spapr
->wds
));
111 trace_spapr_watchdog_expired(num
, w
->action
);
113 case PSERIES_WDTF_ACTION_HARD_POWER_OFF
:
114 qemu_system_vmstop_request(RUN_STATE_SHUTDOWN
);
116 case PSERIES_WDTF_ACTION_HARD_RESTART
:
117 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET
);
119 case PSERIES_WDTF_ACTION_DUMP_RESTART
:
121 async_run_on_cpu(cs
, spapr_do_system_reset_on_cpu
, RUN_ON_CPU_NULL
);
125 if (!w
->leave_others
) {
126 watchdog_stop_all(spapr
);
130 static target_ulong
h_watchdog(PowerPCCPU
*cpu
,
131 SpaprMachineState
*spapr
,
132 target_ulong opcode
, target_ulong
*args
)
134 target_ulong ret
= H_SUCCESS
;
135 target_ulong flags
= args
[0];
136 target_ulong watchdogNumber
= args
[1]; /* 1-Based per PAPR */
137 target_ulong timeoutInMs
= args
[2];
138 unsigned operation
= FIELD_EX64(flags
, PSERIES_WDTF
, OP
);
139 unsigned timeoutAction
= FIELD_EX64(flags
, PSERIES_WDTF
, ACTION
);
142 if (FIELD_EX64(flags
, PSERIES_WDTF
, RESERVED
)) {
147 case PSERIES_WDTF_OP_START
:
148 if (watchdogNumber
> ARRAY_SIZE(spapr
->wds
)) {
151 if (timeoutInMs
<= WDT_MIN_TIMEOUT
) {
155 w
= &spapr
->wds
[watchdogNumber
- 1];
156 switch (timeoutAction
) {
157 case PSERIES_WDTF_ACTION_HARD_POWER_OFF
:
158 case PSERIES_WDTF_ACTION_HARD_RESTART
:
159 case PSERIES_WDTF_ACTION_DUMP_RESTART
:
160 w
->action
= timeoutAction
;
165 w
->leave_others
= FIELD_EX64(flags
, PSERIES_WDTF
, LEAVE_OTHER
);
167 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL
) + timeoutInMs
);
168 trace_spapr_watchdog_start(flags
, watchdogNumber
, timeoutInMs
);
170 case PSERIES_WDTF_OP_STOP
:
171 if (watchdogNumber
== PSERIES_WDT_STOP_ALL
) {
172 ret
= watchdog_stop_all(spapr
);
173 } else if (watchdogNumber
<= ARRAY_SIZE(spapr
->wds
)) {
174 ret
= watchdog_stop(watchdogNumber
,
175 &spapr
->wds
[watchdogNumber
- 1]);
180 case PSERIES_WDTF_OP_QUERY
:
181 args
[0] = FIELD_DP64(0, PSERIES_WDTQ
, MIN_TIMEOUT
, WDT_MIN_TIMEOUT
);
182 args
[0] = FIELD_DP64(args
[0], PSERIES_WDTQ
, NUM
,
183 ARRAY_SIZE(spapr
->wds
));
184 trace_spapr_watchdog_query(args
[0]);
186 case PSERIES_WDTF_OP_QUERY_LPM
:
187 if (watchdogNumber
> ARRAY_SIZE(spapr
->wds
)) {
190 args
[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED
;
191 trace_spapr_watchdog_query_lpm(args
[0]);
200 void spapr_watchdog_init(SpaprMachineState
*spapr
)
204 for (i
= 0; i
< ARRAY_SIZE(spapr
->wds
); ++i
) {
206 SpaprWatchdog
*w
= &spapr
->wds
[i
];
208 snprintf(name
, sizeof(name
) - 1, "wdt%d", i
+ 1);
209 object_initialize_child_with_props(OBJECT(spapr
), name
, w
,
210 sizeof(SpaprWatchdog
),
213 qdev_realize(DEVICE(w
), NULL
, &error_fatal
);
217 static bool watchdog_needed(void *opaque
)
219 SpaprWatchdog
*w
= opaque
;
221 return timer_pending(&w
->timer
);
224 static const VMStateDescription vmstate_wdt
= {
225 .name
= "spapr_watchdog",
227 .minimum_version_id
= 1,
228 .needed
= watchdog_needed
,
229 .fields
= (VMStateField
[]) {
230 VMSTATE_TIMER(timer
, SpaprWatchdog
),
231 VMSTATE_UINT8(action
, SpaprWatchdog
),
232 VMSTATE_UINT8(leave_others
, SpaprWatchdog
),
233 VMSTATE_END_OF_LIST()
237 static void spapr_wdt_realize(DeviceState
*dev
, Error
**errp
)
239 SpaprWatchdog
*w
= SPAPR_WDT(dev
);
240 Object
*o
= OBJECT(dev
);
242 timer_init_ms(&w
->timer
, QEMU_CLOCK_VIRTUAL
, watchdog_expired
, w
);
244 object_property_add_uint64_ptr(o
, "expire",
245 (uint64_t *)&w
->timer
.expire_time
,
247 object_property_add_uint8_ptr(o
, "action", &w
->action
, OBJ_PROP_FLAG_READ
);
248 object_property_add_uint8_ptr(o
, "leaveOtherWatchdogsRunningOnTimeout",
249 &w
->leave_others
, OBJ_PROP_FLAG_READ
);
252 static void spapr_wdt_class_init(ObjectClass
*oc
, void *data
)
254 DeviceClass
*dc
= DEVICE_CLASS(oc
);
256 dc
->realize
= spapr_wdt_realize
;
257 dc
->vmsd
= &vmstate_wdt
;
258 dc
->user_creatable
= false;
261 static const TypeInfo spapr_wdt_info
= {
262 .name
= TYPE_SPAPR_WDT
,
263 .parent
= TYPE_DEVICE
,
264 .instance_size
= sizeof(SpaprWatchdog
),
265 .class_init
= spapr_wdt_class_init
,
268 static void spapr_watchdog_register_types(void)
270 spapr_register_hypercall(H_WATCHDOG
, h_watchdog
);
271 type_register_static(&spapr_wdt_info
);
274 type_init(spapr_watchdog_register_types
)