Annotation of qemu/hw/wdt_i6300esb.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.