1 /* 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. 6 * 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. 11 * 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/>. 14 */ 15 16 #include "qemu/osdep.h" 17 #include "qapi/error.h" 18 #include "target/ppc/cpu.h" 19 #include "migration/vmstate.h" 20 #include "trace.h" 21 22 #include "hw/ppc/spapr.h" 23 24 #define FIELD_BE(reg, field, start, len) \ 25 FIELD(reg, field, 64 - (start + len), len) 26 27 /* 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 32 */ 33 FIELD_BE(PSERIES_WDTF, LEAVE_OTHER, 47, 1) 34 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 41 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 47 48 FIELD_BE(PSERIES_WDTF, RESERVED, 0, 47) 49 50 /* Special watchdogNumber for the "stop all watchdogs" operation */ 51 #define PSERIES_WDT_STOP_ALL ((uint64_t)~0) 52 53 /* 54 * For the "Query watchdog capabilities" operation, a uint64 structure 55 * defined as: 56 * Bits 0-15: The minimum supported timeout in milliseconds 57 * Bits 16-31: The number of watchdogs supported 58 * Bits 32-63: Reserved 59 */ 60 FIELD_BE(PSERIES_WDTQ, MIN_TIMEOUT, 0, 16) 61 FIELD_BE(PSERIES_WDTQ, NUM, 16, 16) 62 63 /* 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 67 * suspending 68 */ 69 #define PSERIES_WDTQL_STOPPED 1 70 #define PSERIES_WDTQL_QUERY_NOT_STOPPED 2 71 72 #define WDT_MIN_TIMEOUT 1 /* 1ms */ 73 74 static target_ulong watchdog_stop(unsigned watchdogNumber, SpaprWatchdog *w) 75 { 76 target_ulong ret = H_NOOP; 77 78 if (timer_pending(&w->timer)) { 79 timer_del(&w->timer); 80 ret = H_SUCCESS; 81 } 82 trace_spapr_watchdog_stop(watchdogNumber, ret); 83 84 return ret; 85 } 86 87 static target_ulong watchdog_stop_all(SpaprMachineState *spapr) 88 { 89 target_ulong ret = H_NOOP; 90 int i; 91 92 for (i = 1; i <= ARRAY_SIZE(spapr->wds); ++i) { 93 target_ulong r = watchdog_stop(i, &spapr->wds[i - 1]); 94 95 if (r != H_NOOP && r != H_SUCCESS) { 96 ret = r; 97 } 98 } 99 100 return ret; 101 } 102 103 static void watchdog_expired(void *pw) 104 { 105 SpaprWatchdog *w = pw; 106 CPUState *cs; 107 SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); 108 unsigned num = w - spapr->wds; 109 110 g_assert(num < ARRAY_SIZE(spapr->wds)); 111 trace_spapr_watchdog_expired(num, w->action); 112 switch (w->action) { 113 case PSERIES_WDTF_ACTION_HARD_POWER_OFF: 114 qemu_system_vmstop_request(RUN_STATE_SHUTDOWN); 115 break; 116 case PSERIES_WDTF_ACTION_HARD_RESTART: 117 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 118 break; 119 case PSERIES_WDTF_ACTION_DUMP_RESTART: 120 CPU_FOREACH(cs) { 121 async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); 122 } 123 break; 124 } 125 if (!w->leave_others) { 126 watchdog_stop_all(spapr); 127 } 128 } 129 130 static target_ulong h_watchdog(PowerPCCPU *cpu, 131 SpaprMachineState *spapr, 132 target_ulong opcode, target_ulong *args) 133 { 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); 140 SpaprWatchdog *w; 141 142 if (FIELD_EX64(flags, PSERIES_WDTF, RESERVED)) { 143 return H_PARAMETER; 144 } 145 146 switch (operation) { 147 case PSERIES_WDTF_OP_START: 148 if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { 149 return H_P2; 150 } 151 if (timeoutInMs <= WDT_MIN_TIMEOUT) { 152 return H_P3; 153 } 154 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; 161 break; 162 default: 163 return H_PARAMETER; 164 } 165 w->leave_others = FIELD_EX64(flags, PSERIES_WDTF, LEAVE_OTHER); 166 timer_mod(&w->timer, 167 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + timeoutInMs); 168 trace_spapr_watchdog_start(flags, watchdogNumber, timeoutInMs); 169 break; 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]); 176 } else { 177 return H_P2; 178 } 179 break; 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]); 185 break; 186 case PSERIES_WDTF_OP_QUERY_LPM: 187 if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { 188 return H_P2; 189 } 190 args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED; 191 trace_spapr_watchdog_query_lpm(args[0]); 192 break; 193 default: 194 return H_PARAMETER; 195 } 196 197 return ret; 198 } 199 200 void spapr_watchdog_init(SpaprMachineState *spapr) 201 { 202 int i; 203 204 for (i = 0; i < ARRAY_SIZE(spapr->wds); ++i) { 205 char name[16]; 206 SpaprWatchdog *w = &spapr->wds[i]; 207 208 snprintf(name, sizeof(name) - 1, "wdt%d", i + 1); 209 object_initialize_child_with_props(OBJECT(spapr), name, w, 210 sizeof(SpaprWatchdog), 211 TYPE_SPAPR_WDT, 212 &error_fatal, NULL); 213 qdev_realize(DEVICE(w), NULL, &error_fatal); 214 } 215 } 216 217 static bool watchdog_needed(void *opaque) 218 { 219 SpaprWatchdog *w = opaque; 220 221 return timer_pending(&w->timer); 222 } 223 224 static const VMStateDescription vmstate_wdt = { 225 .name = "spapr_watchdog", 226 .version_id = 1, 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() 234 } 235 }; 236 237 static void spapr_wdt_realize(DeviceState *dev, Error **errp) 238 { 239 SpaprWatchdog *w = SPAPR_WDT(dev); 240 Object *o = OBJECT(dev); 241 242 timer_init_ms(&w->timer, QEMU_CLOCK_VIRTUAL, watchdog_expired, w); 243 244 object_property_add_uint64_ptr(o, "expire", 245 (uint64_t *)&w->timer.expire_time, 246 OBJ_PROP_FLAG_READ); 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); 250 } 251 252 static void spapr_wdt_class_init(ObjectClass *oc, void *data) 253 { 254 DeviceClass *dc = DEVICE_CLASS(oc); 255 256 dc->realize = spapr_wdt_realize; 257 dc->vmsd = &vmstate_wdt; 258 dc->user_creatable = false; 259 } 260 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, 266 }; 267 268 static void spapr_watchdog_register_types(void) 269 { 270 spapr_register_hypercall(H_WATCHDOG, h_watchdog); 271 type_register_static(&spapr_wdt_info); 272 } 273 274 type_init(spapr_watchdog_register_types) 275