1*81b205ceSAlexey Kardashevskiy /* 2*81b205ceSAlexey Kardashevskiy * This library is free software; you can redistribute it and/or 3*81b205ceSAlexey Kardashevskiy * modify it under the terms of the GNU Lesser General Public 4*81b205ceSAlexey Kardashevskiy * License as published by the Free Software Foundation; either 5*81b205ceSAlexey Kardashevskiy * version 2.1 of the License, or (at your option) any later version. 6*81b205ceSAlexey Kardashevskiy * 7*81b205ceSAlexey Kardashevskiy * This library is distributed in the hope that it will be useful, 8*81b205ceSAlexey Kardashevskiy * but WITHOUT ANY WARRANTY; without even the implied warranty of 9*81b205ceSAlexey Kardashevskiy * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10*81b205ceSAlexey Kardashevskiy * Lesser General Public License for more details. 11*81b205ceSAlexey Kardashevskiy * 12*81b205ceSAlexey Kardashevskiy * You should have received a copy of the GNU Lesser General Public 13*81b205ceSAlexey Kardashevskiy * License along with this library; if not, see <http://www.gnu.org/licenses/>. 14*81b205ceSAlexey Kardashevskiy */ 15*81b205ceSAlexey Kardashevskiy 16*81b205ceSAlexey Kardashevskiy #include "qemu/osdep.h" 17*81b205ceSAlexey Kardashevskiy #include "qapi/error.h" 18*81b205ceSAlexey Kardashevskiy #include "target/ppc/cpu.h" 19*81b205ceSAlexey Kardashevskiy #include "migration/vmstate.h" 20*81b205ceSAlexey Kardashevskiy #include "trace.h" 21*81b205ceSAlexey Kardashevskiy 22*81b205ceSAlexey Kardashevskiy #include "hw/ppc/spapr.h" 23*81b205ceSAlexey Kardashevskiy 24*81b205ceSAlexey Kardashevskiy #define FIELD_BE(reg, field, start, len) \ 25*81b205ceSAlexey Kardashevskiy FIELD(reg, field, 64 - (start + len), len) 26*81b205ceSAlexey Kardashevskiy 27*81b205ceSAlexey Kardashevskiy /* 28*81b205ceSAlexey Kardashevskiy * Bits 47: "leaveOtherWatchdogsRunningOnTimeout", specified on 29*81b205ceSAlexey Kardashevskiy * the "Start watchdog" operation, 30*81b205ceSAlexey Kardashevskiy * 0 - stop out-standing watchdogs on timeout, 31*81b205ceSAlexey Kardashevskiy * 1 - leave outstanding watchdogs running on timeout 32*81b205ceSAlexey Kardashevskiy */ 33*81b205ceSAlexey Kardashevskiy FIELD_BE(PSERIES_WDTF, LEAVE_OTHER, 47, 1) 34*81b205ceSAlexey Kardashevskiy 35*81b205ceSAlexey Kardashevskiy /* Bits 48-55: "operation" */ 36*81b205ceSAlexey Kardashevskiy FIELD_BE(PSERIES_WDTF, OP, 48, 8) 37*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTF_OP_START 0x1 38*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTF_OP_STOP 0x2 39*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTF_OP_QUERY 0x3 40*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTF_OP_QUERY_LPM 0x4 41*81b205ceSAlexey Kardashevskiy 42*81b205ceSAlexey Kardashevskiy /* Bits 56-63: "timeoutAction" */ 43*81b205ceSAlexey Kardashevskiy FIELD_BE(PSERIES_WDTF, ACTION, 56, 8) 44*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTF_ACTION_HARD_POWER_OFF 0x1 45*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTF_ACTION_HARD_RESTART 0x2 46*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3 47*81b205ceSAlexey Kardashevskiy 48*81b205ceSAlexey Kardashevskiy FIELD_BE(PSERIES_WDTF, RESERVED, 0, 47) 49*81b205ceSAlexey Kardashevskiy 50*81b205ceSAlexey Kardashevskiy /* Special watchdogNumber for the "stop all watchdogs" operation */ 51*81b205ceSAlexey Kardashevskiy #define PSERIES_WDT_STOP_ALL ((uint64_t)~0) 52*81b205ceSAlexey Kardashevskiy 53*81b205ceSAlexey Kardashevskiy /* 54*81b205ceSAlexey Kardashevskiy * For the "Query watchdog capabilities" operation, a uint64 structure 55*81b205ceSAlexey Kardashevskiy * defined as: 56*81b205ceSAlexey Kardashevskiy * Bits 0-15: The minimum supported timeout in milliseconds 57*81b205ceSAlexey Kardashevskiy * Bits 16-31: The number of watchdogs supported 58*81b205ceSAlexey Kardashevskiy * Bits 32-63: Reserved 59*81b205ceSAlexey Kardashevskiy */ 60*81b205ceSAlexey Kardashevskiy FIELD_BE(PSERIES_WDTQ, MIN_TIMEOUT, 0, 16) 61*81b205ceSAlexey Kardashevskiy FIELD_BE(PSERIES_WDTQ, NUM, 16, 16) 62*81b205ceSAlexey Kardashevskiy 63*81b205ceSAlexey Kardashevskiy /* 64*81b205ceSAlexey Kardashevskiy * For the "Query watchdog LPM requirement" operation: 65*81b205ceSAlexey Kardashevskiy * 1 = The given "watchdogNumber" must be stopped prior to suspending 66*81b205ceSAlexey Kardashevskiy * 2 = The given "watchdogNumber" does not have to be stopped prior to 67*81b205ceSAlexey Kardashevskiy * suspending 68*81b205ceSAlexey Kardashevskiy */ 69*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTQL_STOPPED 1 70*81b205ceSAlexey Kardashevskiy #define PSERIES_WDTQL_QUERY_NOT_STOPPED 2 71*81b205ceSAlexey Kardashevskiy 72*81b205ceSAlexey Kardashevskiy #define WDT_MIN_TIMEOUT 1 /* 1ms */ 73*81b205ceSAlexey Kardashevskiy 74*81b205ceSAlexey Kardashevskiy static target_ulong watchdog_stop(unsigned watchdogNumber, SpaprWatchdog *w) 75*81b205ceSAlexey Kardashevskiy { 76*81b205ceSAlexey Kardashevskiy target_ulong ret = H_NOOP; 77*81b205ceSAlexey Kardashevskiy 78*81b205ceSAlexey Kardashevskiy if (timer_pending(&w->timer)) { 79*81b205ceSAlexey Kardashevskiy timer_del(&w->timer); 80*81b205ceSAlexey Kardashevskiy ret = H_SUCCESS; 81*81b205ceSAlexey Kardashevskiy } 82*81b205ceSAlexey Kardashevskiy trace_spapr_watchdog_stop(watchdogNumber, ret); 83*81b205ceSAlexey Kardashevskiy 84*81b205ceSAlexey Kardashevskiy return ret; 85*81b205ceSAlexey Kardashevskiy } 86*81b205ceSAlexey Kardashevskiy 87*81b205ceSAlexey Kardashevskiy static target_ulong watchdog_stop_all(SpaprMachineState *spapr) 88*81b205ceSAlexey Kardashevskiy { 89*81b205ceSAlexey Kardashevskiy target_ulong ret = H_NOOP; 90*81b205ceSAlexey Kardashevskiy int i; 91*81b205ceSAlexey Kardashevskiy 92*81b205ceSAlexey Kardashevskiy for (i = 1; i <= ARRAY_SIZE(spapr->wds); ++i) { 93*81b205ceSAlexey Kardashevskiy target_ulong r = watchdog_stop(i, &spapr->wds[i - 1]); 94*81b205ceSAlexey Kardashevskiy 95*81b205ceSAlexey Kardashevskiy if (r != H_NOOP && r != H_SUCCESS) { 96*81b205ceSAlexey Kardashevskiy ret = r; 97*81b205ceSAlexey Kardashevskiy } 98*81b205ceSAlexey Kardashevskiy } 99*81b205ceSAlexey Kardashevskiy 100*81b205ceSAlexey Kardashevskiy return ret; 101*81b205ceSAlexey Kardashevskiy } 102*81b205ceSAlexey Kardashevskiy 103*81b205ceSAlexey Kardashevskiy static void watchdog_expired(void *pw) 104*81b205ceSAlexey Kardashevskiy { 105*81b205ceSAlexey Kardashevskiy SpaprWatchdog *w = pw; 106*81b205ceSAlexey Kardashevskiy CPUState *cs; 107*81b205ceSAlexey Kardashevskiy SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); 108*81b205ceSAlexey Kardashevskiy unsigned num = w - spapr->wds; 109*81b205ceSAlexey Kardashevskiy 110*81b205ceSAlexey Kardashevskiy g_assert(num < ARRAY_SIZE(spapr->wds)); 111*81b205ceSAlexey Kardashevskiy trace_spapr_watchdog_expired(num, w->action); 112*81b205ceSAlexey Kardashevskiy switch (w->action) { 113*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_ACTION_HARD_POWER_OFF: 114*81b205ceSAlexey Kardashevskiy qemu_system_vmstop_request(RUN_STATE_SHUTDOWN); 115*81b205ceSAlexey Kardashevskiy break; 116*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_ACTION_HARD_RESTART: 117*81b205ceSAlexey Kardashevskiy qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 118*81b205ceSAlexey Kardashevskiy break; 119*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_ACTION_DUMP_RESTART: 120*81b205ceSAlexey Kardashevskiy CPU_FOREACH(cs) { 121*81b205ceSAlexey Kardashevskiy async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); 122*81b205ceSAlexey Kardashevskiy } 123*81b205ceSAlexey Kardashevskiy break; 124*81b205ceSAlexey Kardashevskiy } 125*81b205ceSAlexey Kardashevskiy if (!w->leave_others) { 126*81b205ceSAlexey Kardashevskiy watchdog_stop_all(spapr); 127*81b205ceSAlexey Kardashevskiy } 128*81b205ceSAlexey Kardashevskiy } 129*81b205ceSAlexey Kardashevskiy 130*81b205ceSAlexey Kardashevskiy static target_ulong h_watchdog(PowerPCCPU *cpu, 131*81b205ceSAlexey Kardashevskiy SpaprMachineState *spapr, 132*81b205ceSAlexey Kardashevskiy target_ulong opcode, target_ulong *args) 133*81b205ceSAlexey Kardashevskiy { 134*81b205ceSAlexey Kardashevskiy target_ulong ret = H_SUCCESS; 135*81b205ceSAlexey Kardashevskiy target_ulong flags = args[0]; 136*81b205ceSAlexey Kardashevskiy target_ulong watchdogNumber = args[1]; /* 1-Based per PAPR */ 137*81b205ceSAlexey Kardashevskiy target_ulong timeoutInMs = args[2]; 138*81b205ceSAlexey Kardashevskiy unsigned operation = FIELD_EX64(flags, PSERIES_WDTF, OP); 139*81b205ceSAlexey Kardashevskiy unsigned timeoutAction = FIELD_EX64(flags, PSERIES_WDTF, ACTION); 140*81b205ceSAlexey Kardashevskiy SpaprWatchdog *w; 141*81b205ceSAlexey Kardashevskiy 142*81b205ceSAlexey Kardashevskiy if (FIELD_EX64(flags, PSERIES_WDTF, RESERVED)) { 143*81b205ceSAlexey Kardashevskiy return H_PARAMETER; 144*81b205ceSAlexey Kardashevskiy } 145*81b205ceSAlexey Kardashevskiy 146*81b205ceSAlexey Kardashevskiy switch (operation) { 147*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_OP_START: 148*81b205ceSAlexey Kardashevskiy if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { 149*81b205ceSAlexey Kardashevskiy return H_P2; 150*81b205ceSAlexey Kardashevskiy } 151*81b205ceSAlexey Kardashevskiy if (timeoutInMs <= WDT_MIN_TIMEOUT) { 152*81b205ceSAlexey Kardashevskiy return H_P3; 153*81b205ceSAlexey Kardashevskiy } 154*81b205ceSAlexey Kardashevskiy 155*81b205ceSAlexey Kardashevskiy w = &spapr->wds[watchdogNumber - 1]; 156*81b205ceSAlexey Kardashevskiy switch (timeoutAction) { 157*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_ACTION_HARD_POWER_OFF: 158*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_ACTION_HARD_RESTART: 159*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_ACTION_DUMP_RESTART: 160*81b205ceSAlexey Kardashevskiy w->action = timeoutAction; 161*81b205ceSAlexey Kardashevskiy break; 162*81b205ceSAlexey Kardashevskiy default: 163*81b205ceSAlexey Kardashevskiy return H_PARAMETER; 164*81b205ceSAlexey Kardashevskiy } 165*81b205ceSAlexey Kardashevskiy w->leave_others = FIELD_EX64(flags, PSERIES_WDTF, LEAVE_OTHER); 166*81b205ceSAlexey Kardashevskiy timer_mod(&w->timer, 167*81b205ceSAlexey Kardashevskiy qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + timeoutInMs); 168*81b205ceSAlexey Kardashevskiy trace_spapr_watchdog_start(flags, watchdogNumber, timeoutInMs); 169*81b205ceSAlexey Kardashevskiy break; 170*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_OP_STOP: 171*81b205ceSAlexey Kardashevskiy if (watchdogNumber == PSERIES_WDT_STOP_ALL) { 172*81b205ceSAlexey Kardashevskiy ret = watchdog_stop_all(spapr); 173*81b205ceSAlexey Kardashevskiy } else if (watchdogNumber <= ARRAY_SIZE(spapr->wds)) { 174*81b205ceSAlexey Kardashevskiy ret = watchdog_stop(watchdogNumber, 175*81b205ceSAlexey Kardashevskiy &spapr->wds[watchdogNumber - 1]); 176*81b205ceSAlexey Kardashevskiy } else { 177*81b205ceSAlexey Kardashevskiy return H_P2; 178*81b205ceSAlexey Kardashevskiy } 179*81b205ceSAlexey Kardashevskiy break; 180*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_OP_QUERY: 181*81b205ceSAlexey Kardashevskiy args[0] = FIELD_DP64(0, PSERIES_WDTQ, MIN_TIMEOUT, WDT_MIN_TIMEOUT); 182*81b205ceSAlexey Kardashevskiy args[0] = FIELD_DP64(args[0], PSERIES_WDTQ, NUM, 183*81b205ceSAlexey Kardashevskiy ARRAY_SIZE(spapr->wds)); 184*81b205ceSAlexey Kardashevskiy trace_spapr_watchdog_query(args[0]); 185*81b205ceSAlexey Kardashevskiy break; 186*81b205ceSAlexey Kardashevskiy case PSERIES_WDTF_OP_QUERY_LPM: 187*81b205ceSAlexey Kardashevskiy if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { 188*81b205ceSAlexey Kardashevskiy return H_P2; 189*81b205ceSAlexey Kardashevskiy } 190*81b205ceSAlexey Kardashevskiy args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED; 191*81b205ceSAlexey Kardashevskiy trace_spapr_watchdog_query_lpm(args[0]); 192*81b205ceSAlexey Kardashevskiy break; 193*81b205ceSAlexey Kardashevskiy default: 194*81b205ceSAlexey Kardashevskiy return H_PARAMETER; 195*81b205ceSAlexey Kardashevskiy } 196*81b205ceSAlexey Kardashevskiy 197*81b205ceSAlexey Kardashevskiy return ret; 198*81b205ceSAlexey Kardashevskiy } 199*81b205ceSAlexey Kardashevskiy 200*81b205ceSAlexey Kardashevskiy void spapr_watchdog_init(SpaprMachineState *spapr) 201*81b205ceSAlexey Kardashevskiy { 202*81b205ceSAlexey Kardashevskiy int i; 203*81b205ceSAlexey Kardashevskiy 204*81b205ceSAlexey Kardashevskiy for (i = 0; i < ARRAY_SIZE(spapr->wds); ++i) { 205*81b205ceSAlexey Kardashevskiy char name[16]; 206*81b205ceSAlexey Kardashevskiy SpaprWatchdog *w = &spapr->wds[i]; 207*81b205ceSAlexey Kardashevskiy 208*81b205ceSAlexey Kardashevskiy snprintf(name, sizeof(name) - 1, "wdt%d", i + 1); 209*81b205ceSAlexey Kardashevskiy object_initialize_child_with_props(OBJECT(spapr), name, w, 210*81b205ceSAlexey Kardashevskiy sizeof(SpaprWatchdog), 211*81b205ceSAlexey Kardashevskiy TYPE_SPAPR_WDT, 212*81b205ceSAlexey Kardashevskiy &error_fatal, NULL); 213*81b205ceSAlexey Kardashevskiy qdev_realize(DEVICE(w), NULL, &error_fatal); 214*81b205ceSAlexey Kardashevskiy } 215*81b205ceSAlexey Kardashevskiy } 216*81b205ceSAlexey Kardashevskiy 217*81b205ceSAlexey Kardashevskiy static bool watchdog_needed(void *opaque) 218*81b205ceSAlexey Kardashevskiy { 219*81b205ceSAlexey Kardashevskiy SpaprWatchdog *w = opaque; 220*81b205ceSAlexey Kardashevskiy 221*81b205ceSAlexey Kardashevskiy return timer_pending(&w->timer); 222*81b205ceSAlexey Kardashevskiy } 223*81b205ceSAlexey Kardashevskiy 224*81b205ceSAlexey Kardashevskiy static const VMStateDescription vmstate_wdt = { 225*81b205ceSAlexey Kardashevskiy .name = "spapr_watchdog", 226*81b205ceSAlexey Kardashevskiy .version_id = 1, 227*81b205ceSAlexey Kardashevskiy .minimum_version_id = 1, 228*81b205ceSAlexey Kardashevskiy .needed = watchdog_needed, 229*81b205ceSAlexey Kardashevskiy .fields = (VMStateField[]) { 230*81b205ceSAlexey Kardashevskiy VMSTATE_TIMER(timer, SpaprWatchdog), 231*81b205ceSAlexey Kardashevskiy VMSTATE_UINT8(action, SpaprWatchdog), 232*81b205ceSAlexey Kardashevskiy VMSTATE_UINT8(leave_others, SpaprWatchdog), 233*81b205ceSAlexey Kardashevskiy VMSTATE_END_OF_LIST() 234*81b205ceSAlexey Kardashevskiy } 235*81b205ceSAlexey Kardashevskiy }; 236*81b205ceSAlexey Kardashevskiy 237*81b205ceSAlexey Kardashevskiy static void spapr_wdt_realize(DeviceState *dev, Error **errp) 238*81b205ceSAlexey Kardashevskiy { 239*81b205ceSAlexey Kardashevskiy SpaprWatchdog *w = SPAPR_WDT(dev); 240*81b205ceSAlexey Kardashevskiy Object *o = OBJECT(dev); 241*81b205ceSAlexey Kardashevskiy 242*81b205ceSAlexey Kardashevskiy timer_init_ms(&w->timer, QEMU_CLOCK_VIRTUAL, watchdog_expired, w); 243*81b205ceSAlexey Kardashevskiy 244*81b205ceSAlexey Kardashevskiy object_property_add_uint64_ptr(o, "expire", 245*81b205ceSAlexey Kardashevskiy (uint64_t *)&w->timer.expire_time, 246*81b205ceSAlexey Kardashevskiy OBJ_PROP_FLAG_READ); 247*81b205ceSAlexey Kardashevskiy object_property_add_uint8_ptr(o, "action", &w->action, OBJ_PROP_FLAG_READ); 248*81b205ceSAlexey Kardashevskiy object_property_add_uint8_ptr(o, "leaveOtherWatchdogsRunningOnTimeout", 249*81b205ceSAlexey Kardashevskiy &w->leave_others, OBJ_PROP_FLAG_READ); 250*81b205ceSAlexey Kardashevskiy } 251*81b205ceSAlexey Kardashevskiy 252*81b205ceSAlexey Kardashevskiy static void spapr_wdt_class_init(ObjectClass *oc, void *data) 253*81b205ceSAlexey Kardashevskiy { 254*81b205ceSAlexey Kardashevskiy DeviceClass *dc = DEVICE_CLASS(oc); 255*81b205ceSAlexey Kardashevskiy 256*81b205ceSAlexey Kardashevskiy dc->realize = spapr_wdt_realize; 257*81b205ceSAlexey Kardashevskiy dc->vmsd = &vmstate_wdt; 258*81b205ceSAlexey Kardashevskiy dc->user_creatable = false; 259*81b205ceSAlexey Kardashevskiy } 260*81b205ceSAlexey Kardashevskiy 261*81b205ceSAlexey Kardashevskiy static const TypeInfo spapr_wdt_info = { 262*81b205ceSAlexey Kardashevskiy .name = TYPE_SPAPR_WDT, 263*81b205ceSAlexey Kardashevskiy .parent = TYPE_DEVICE, 264*81b205ceSAlexey Kardashevskiy .instance_size = sizeof(SpaprWatchdog), 265*81b205ceSAlexey Kardashevskiy .class_init = spapr_wdt_class_init, 266*81b205ceSAlexey Kardashevskiy }; 267*81b205ceSAlexey Kardashevskiy 268*81b205ceSAlexey Kardashevskiy static void spapr_watchdog_register_types(void) 269*81b205ceSAlexey Kardashevskiy { 270*81b205ceSAlexey Kardashevskiy spapr_register_hypercall(H_WATCHDOG, h_watchdog); 271*81b205ceSAlexey Kardashevskiy type_register_static(&spapr_wdt_info); 272*81b205ceSAlexey Kardashevskiy } 273*81b205ceSAlexey Kardashevskiy 274*81b205ceSAlexey Kardashevskiy type_init(spapr_watchdog_register_types) 275