|
|
1.1 ! root 1: /* ! 2: * Virtual hardware watchdog. ! 3: * ! 4: * Copyright (C) 2009 Red Hat Inc. ! 5: * ! 6: * This program is free software; you can redistribute it and/or ! 7: * modify it under the terms of the GNU General Public License ! 8: * as published by the Free Software Foundation; either version 2 ! 9: * of the License, or (at your option) any later version. ! 10: * ! 11: * This program is distributed in the hope that it will be useful, ! 12: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 14: * GNU General Public License for more details. ! 15: * ! 16: * You should have received a copy of the GNU General Public License ! 17: * along with this program; if not, see <http://www.gnu.org/licenses/>. ! 18: * ! 19: * By Richard W.M. Jones ([email protected]). ! 20: */ ! 21: ! 22: #include <inttypes.h> ! 23: ! 24: #include "qemu-common.h" ! 25: #include "qemu-timer.h" ! 26: #include "watchdog.h" ! 27: #include "hw.h" ! 28: #include "isa.h" ! 29: #include "pc.h" ! 30: #include "pci.h" ! 31: ! 32: /*#define I6300ESB_DEBUG 1*/ ! 33: ! 34: #ifdef I6300ESB_DEBUG ! 35: #define i6300esb_debug(fs,...) \ ! 36: fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__) ! 37: #else ! 38: #define i6300esb_debug(fs,...) ! 39: #endif ! 40: ! 41: #ifndef PCI_DEVICE_ID_INTEL_ESB_9 ! 42: #define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab ! 43: #endif ! 44: ! 45: /* PCI configuration registers */ ! 46: #define ESB_CONFIG_REG 0x60 /* Config register */ ! 47: #define ESB_LOCK_REG 0x68 /* WDT lock register */ ! 48: ! 49: /* Memory mapped registers (offset from base address) */ ! 50: #define ESB_TIMER1_REG 0x00 /* Timer1 value after each reset */ ! 51: #define ESB_TIMER2_REG 0x04 /* Timer2 value after each reset */ ! 52: #define ESB_GINTSR_REG 0x08 /* General Interrupt Status Register */ ! 53: #define ESB_RELOAD_REG 0x0c /* Reload register */ ! 54: ! 55: /* Lock register bits */ ! 56: #define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */ ! 57: #define ESB_WDT_ENABLE (0x01 << 1) /* Enable WDT */ ! 58: #define ESB_WDT_LOCK (0x01 << 0) /* Lock (nowayout) */ ! 59: ! 60: /* Config register bits */ ! 61: #define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */ ! 62: #define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */ ! 63: #define ESB_WDT_INTTYPE (0x11 << 0) /* Interrupt type on timer1 timeout */ ! 64: ! 65: /* Reload register bits */ ! 66: #define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */ ! 67: ! 68: /* Magic constants */ ! 69: #define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ ! 70: #define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ ! 71: ! 72: /* Device state. */ ! 73: struct I6300State { ! 74: PCIDevice dev; /* PCI device state, must be first field. */ ! 75: ! 76: int reboot_enabled; /* "Reboot" on timer expiry. The real action ! 77: * performed depends on the -watchdog-action ! 78: * param passed on QEMU command line. ! 79: */ ! 80: int clock_scale; /* Clock scale. */ ! 81: #define CLOCK_SCALE_1KHZ 0 ! 82: #define CLOCK_SCALE_1MHZ 1 ! 83: ! 84: int int_type; /* Interrupt type generated. */ ! 85: #define INT_TYPE_IRQ 0 /* APIC 1, INT 10 */ ! 86: #define INT_TYPE_SMI 2 ! 87: #define INT_TYPE_DISABLED 3 ! 88: ! 89: int free_run; /* If true, reload timer on expiry. */ ! 90: int locked; /* If true, enabled field cannot be changed. */ ! 91: int enabled; /* If true, watchdog is enabled. */ ! 92: ! 93: QEMUTimer *timer; /* The actual watchdog timer. */ ! 94: ! 95: uint32_t timer1_preload; /* Values preloaded into timer1, timer2. */ ! 96: uint32_t timer2_preload; ! 97: int stage; /* Stage (1 or 2). */ ! 98: ! 99: int unlock_state; /* Guest writes 0x80, 0x86 to unlock the ! 100: * registers, and we transition through ! 101: * states 0 -> 1 -> 2 when this happens. ! 102: */ ! 103: ! 104: int previous_reboot_flag; /* If the watchdog caused the previous ! 105: * reboot, this flag will be set. ! 106: */ ! 107: }; ! 108: ! 109: typedef struct I6300State I6300State; ! 110: ! 111: /* This function is called when the watchdog has either been enabled ! 112: * (hence it starts counting down) or has been keep-alived. ! 113: */ ! 114: static void i6300esb_restart_timer(I6300State *d, int stage) ! 115: { ! 116: int64_t timeout; ! 117: ! 118: if (!d->enabled) ! 119: return; ! 120: ! 121: d->stage = stage; ! 122: ! 123: if (d->stage <= 1) ! 124: timeout = d->timer1_preload; ! 125: else ! 126: timeout = d->timer2_preload; ! 127: ! 128: if (d->clock_scale == CLOCK_SCALE_1KHZ) ! 129: timeout <<= 15; ! 130: else ! 131: timeout <<= 5; ! 132: ! 133: /* Get the timeout in units of ticks_per_sec. */ ! 134: timeout = ticks_per_sec * timeout / 33000000; ! 135: ! 136: i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout); ! 137: ! 138: qemu_mod_timer(d->timer, qemu_get_clock(vm_clock) + timeout); ! 139: } ! 140: ! 141: /* This is called when the guest disables the watchdog. */ ! 142: static void i6300esb_disable_timer(I6300State *d) ! 143: { ! 144: i6300esb_debug("timer disabled\n"); ! 145: ! 146: qemu_del_timer(d->timer); ! 147: } ! 148: ! 149: static void i6300esb_reset(I6300State *d) ! 150: { ! 151: /* XXX We should probably reset other parts of the state here, ! 152: * but we should also reset our state on general machine reset ! 153: * too. For now just disable the timer so it doesn't fire ! 154: * again after the reboot. ! 155: */ ! 156: i6300esb_disable_timer(d); ! 157: } ! 158: ! 159: /* This function is called when the watchdog expires. Note that ! 160: * the hardware has two timers, and so expiry happens in two stages. ! 161: * If d->stage == 1 then we perform the first stage action (usually, ! 162: * sending an interrupt) and then restart the timer again for the ! 163: * second stage. If the second stage expires then the watchdog ! 164: * really has run out. ! 165: */ ! 166: static void i6300esb_timer_expired(void *vp) ! 167: { ! 168: I6300State *d = (I6300State *) vp; ! 169: ! 170: i6300esb_debug("stage %d\n", d->stage); ! 171: ! 172: if (d->stage == 1) { ! 173: /* What to do at the end of stage 1? */ ! 174: switch (d->int_type) { ! 175: case INT_TYPE_IRQ: ! 176: fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n"); ! 177: break; ! 178: case INT_TYPE_SMI: ! 179: fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n"); ! 180: break; ! 181: } ! 182: ! 183: /* Start the second stage. */ ! 184: i6300esb_restart_timer(d, 2); ! 185: } else { ! 186: /* Second stage expired, reboot for real. */ ! 187: if (d->reboot_enabled) { ! 188: d->previous_reboot_flag = 1; ! 189: watchdog_perform_action(); /* This reboots, exits, etc */ ! 190: i6300esb_reset(d); ! 191: } ! 192: ! 193: /* In "free running mode" we start stage 1 again. */ ! 194: if (d->free_run) ! 195: i6300esb_restart_timer(d, 1); ! 196: } ! 197: } ! 198: ! 199: static void i6300esb_config_write(PCIDevice *dev, uint32_t addr, ! 200: uint32_t data, int len) ! 201: { ! 202: I6300State *d = (I6300State *) dev; ! 203: int old; ! 204: ! 205: i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len); ! 206: ! 207: if (addr == ESB_CONFIG_REG && len == 2) { ! 208: d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0; ! 209: d->clock_scale = ! 210: (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ; ! 211: d->int_type = (data & ESB_WDT_INTTYPE); ! 212: } else if (addr == ESB_LOCK_REG && len == 1) { ! 213: if (!d->locked) { ! 214: d->locked = (data & ESB_WDT_LOCK) != 0; ! 215: d->free_run = (data & ESB_WDT_FUNC) != 0; ! 216: old = d->enabled; ! 217: d->enabled = (data & ESB_WDT_ENABLE) != 0; ! 218: if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */ ! 219: i6300esb_restart_timer(d, 1); ! 220: else if (!d->enabled) ! 221: i6300esb_disable_timer(d); ! 222: } ! 223: } else { ! 224: pci_default_write_config(dev, addr, data, len); ! 225: } ! 226: } ! 227: ! 228: static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len) ! 229: { ! 230: I6300State *d = (I6300State *) dev; ! 231: uint32_t data; ! 232: ! 233: i6300esb_debug ("addr = %x, len = %d\n", addr, len); ! 234: ! 235: if (addr == ESB_CONFIG_REG && len == 2) { ! 236: data = ! 237: (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) | ! 238: (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) | ! 239: d->int_type; ! 240: return data; ! 241: } else if (addr == ESB_LOCK_REG && len == 1) { ! 242: data = ! 243: (d->free_run ? ESB_WDT_FUNC : 0) | ! 244: (d->locked ? ESB_WDT_LOCK : 0) | ! 245: (d->enabled ? ESB_WDT_ENABLE : 0); ! 246: return data; ! 247: } else { ! 248: return pci_default_read_config(dev, addr, len); ! 249: } ! 250: } ! 251: ! 252: static uint32_t i6300esb_mem_readb(void *vp, target_phys_addr_t addr) ! 253: { ! 254: i6300esb_debug ("addr = %x\n", (int) addr); ! 255: ! 256: return 0; ! 257: } ! 258: ! 259: static uint32_t i6300esb_mem_readw(void *vp, target_phys_addr_t addr) ! 260: { ! 261: uint32_t data = 0; ! 262: I6300State *d = (I6300State *) vp; ! 263: ! 264: i6300esb_debug("addr = %x\n", (int) addr); ! 265: ! 266: if (addr == 0xc) { ! 267: /* The previous reboot flag is really bit 9, but there is ! 268: * a bug in the Linux driver where it thinks it's bit 12. ! 269: * Set both. ! 270: */ ! 271: data = d->previous_reboot_flag ? 0x1200 : 0; ! 272: } ! 273: ! 274: return data; ! 275: } ! 276: ! 277: static uint32_t i6300esb_mem_readl(void *vp, target_phys_addr_t addr) ! 278: { ! 279: i6300esb_debug("addr = %x\n", (int) addr); ! 280: ! 281: return 0; ! 282: } ! 283: ! 284: static void i6300esb_mem_writeb(void *vp, target_phys_addr_t addr, uint32_t val) ! 285: { ! 286: I6300State *d = (I6300State *) vp; ! 287: ! 288: i6300esb_debug("addr = %x, val = %x\n", (int) addr, val); ! 289: ! 290: if (addr == 0xc && val == 0x80) ! 291: d->unlock_state = 1; ! 292: else if (addr == 0xc && val == 0x86 && d->unlock_state == 1) ! 293: d->unlock_state = 2; ! 294: } ! 295: ! 296: static void i6300esb_mem_writew(void *vp, target_phys_addr_t addr, uint32_t val) ! 297: { ! 298: I6300State *d = (I6300State *) vp; ! 299: ! 300: i6300esb_debug("addr = %x, val = %x\n", (int) addr, val); ! 301: ! 302: if (addr == 0xc && val == 0x80) ! 303: d->unlock_state = 1; ! 304: else if (addr == 0xc && val == 0x86 && d->unlock_state == 1) ! 305: d->unlock_state = 2; ! 306: else { ! 307: if (d->unlock_state == 2) { ! 308: if (addr == 0xc) { ! 309: if ((val & 0x100) != 0) ! 310: /* This is the "ping" from the userspace watchdog in ! 311: * the guest ... ! 312: */ ! 313: i6300esb_restart_timer(d, 1); ! 314: ! 315: /* Setting bit 9 resets the previous reboot flag. ! 316: * There's a bug in the Linux driver where it sets ! 317: * bit 12 instead. ! 318: */ ! 319: if ((val & 0x200) != 0 || (val & 0x1000) != 0) { ! 320: d->previous_reboot_flag = 0; ! 321: } ! 322: } ! 323: ! 324: d->unlock_state = 0; ! 325: } ! 326: } ! 327: } ! 328: ! 329: static void i6300esb_mem_writel(void *vp, target_phys_addr_t addr, uint32_t val) ! 330: { ! 331: I6300State *d = (I6300State *) vp; ! 332: ! 333: i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val); ! 334: ! 335: if (addr == 0xc && val == 0x80) ! 336: d->unlock_state = 1; ! 337: else if (addr == 0xc && val == 0x86 && d->unlock_state == 1) ! 338: d->unlock_state = 2; ! 339: else { ! 340: if (d->unlock_state == 2) { ! 341: if (addr == 0) ! 342: d->timer1_preload = val & 0xfffff; ! 343: else if (addr == 4) ! 344: d->timer2_preload = val & 0xfffff; ! 345: ! 346: d->unlock_state = 0; ! 347: } ! 348: } ! 349: } ! 350: ! 351: static void i6300esb_map(PCIDevice *dev, int region_num, ! 352: uint32_t addr, uint32_t size, int type) ! 353: { ! 354: static CPUReadMemoryFunc *mem_read[3] = { ! 355: i6300esb_mem_readb, ! 356: i6300esb_mem_readw, ! 357: i6300esb_mem_readl, ! 358: }; ! 359: static CPUWriteMemoryFunc *mem_write[3] = { ! 360: i6300esb_mem_writeb, ! 361: i6300esb_mem_writew, ! 362: i6300esb_mem_writel, ! 363: }; ! 364: I6300State *d = (I6300State *) dev; ! 365: int io_mem; ! 366: ! 367: i6300esb_debug("addr = %x, size = %x, type = %d\n", addr, size, type); ! 368: ! 369: io_mem = cpu_register_io_memory(mem_read, mem_write, d); ! 370: cpu_register_physical_memory (addr, 0x10, io_mem); ! 371: /* qemu_register_coalesced_mmio (addr, 0x10); ? */ ! 372: } ! 373: ! 374: static void i6300esb_save(QEMUFile *f, void *vp) ! 375: { ! 376: I6300State *d = (I6300State *) vp; ! 377: ! 378: pci_device_save(&d->dev, f); ! 379: qemu_put_be32(f, d->reboot_enabled); ! 380: qemu_put_be32(f, d->clock_scale); ! 381: qemu_put_be32(f, d->int_type); ! 382: qemu_put_be32(f, d->free_run); ! 383: qemu_put_be32(f, d->locked); ! 384: qemu_put_be32(f, d->enabled); ! 385: qemu_put_timer(f, d->timer); ! 386: qemu_put_be32(f, d->timer1_preload); ! 387: qemu_put_be32(f, d->timer2_preload); ! 388: qemu_put_be32(f, d->stage); ! 389: qemu_put_be32(f, d->unlock_state); ! 390: qemu_put_be32(f, d->previous_reboot_flag); ! 391: } ! 392: ! 393: static int i6300esb_load(QEMUFile *f, void *vp, int version) ! 394: { ! 395: I6300State *d = (I6300State *) vp; ! 396: ! 397: if (version != sizeof (I6300State)) ! 398: return -EINVAL; ! 399: ! 400: pci_device_load(&d->dev, f); ! 401: d->reboot_enabled = qemu_get_be32(f); ! 402: d->clock_scale = qemu_get_be32(f); ! 403: d->int_type = qemu_get_be32(f); ! 404: d->free_run = qemu_get_be32(f); ! 405: d->locked = qemu_get_be32(f); ! 406: d->enabled = qemu_get_be32(f); ! 407: qemu_get_timer(f, d->timer); ! 408: d->timer1_preload = qemu_get_be32(f); ! 409: d->timer2_preload = qemu_get_be32(f); ! 410: d->stage = qemu_get_be32(f); ! 411: d->unlock_state = qemu_get_be32(f); ! 412: d->previous_reboot_flag = qemu_get_be32(f); ! 413: ! 414: return 0; ! 415: } ! 416: ! 417: /* Create and initialize a virtual Intel 6300ESB during PC creation. */ ! 418: static void i6300esb_pc_init(PCIBus *pci_bus) ! 419: { ! 420: I6300State *d; ! 421: uint8_t *pci_conf; ! 422: ! 423: if (!pci_bus) { ! 424: fprintf(stderr, "wdt_i6300esb: no PCI bus in this machine\n"); ! 425: return; ! 426: } ! 427: ! 428: d = (I6300State *) ! 429: pci_register_device (pci_bus, "i6300esb_wdt", sizeof (I6300State), ! 430: -1, ! 431: i6300esb_config_read, i6300esb_config_write); ! 432: ! 433: d->reboot_enabled = 1; ! 434: d->clock_scale = CLOCK_SCALE_1KHZ; ! 435: d->int_type = INT_TYPE_IRQ; ! 436: d->free_run = 0; ! 437: d->locked = 0; ! 438: d->enabled = 0; ! 439: d->timer = qemu_new_timer(vm_clock, i6300esb_timer_expired, d); ! 440: d->timer1_preload = 0xfffff; ! 441: d->timer2_preload = 0xfffff; ! 442: d->stage = 1; ! 443: d->unlock_state = 0; ! 444: d->previous_reboot_flag = 0; ! 445: ! 446: pci_conf = d->dev.config; ! 447: pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); ! 448: pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_ESB_9); ! 449: pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER); ! 450: pci_conf[0x0e] = 0x00; ! 451: ! 452: pci_register_bar(&d->dev, 0, 0x10, ! 453: PCI_ADDRESS_SPACE_MEM, i6300esb_map); ! 454: ! 455: register_savevm("i6300esb_wdt", -1, sizeof(I6300State), ! 456: i6300esb_save, i6300esb_load, d); ! 457: } ! 458: ! 459: static WatchdogTimerModel model = { ! 460: .wdt_name = "i6300esb", ! 461: .wdt_description = "Intel 6300ESB", ! 462: .wdt_pc_init = i6300esb_pc_init, ! 463: }; ! 464: ! 465: void wdt_i6300esb_init(void) ! 466: { ! 467: watchdog_add_model(&model); ! 468: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.