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

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 "pci.h"
                     29: 
                     30: /*#define I6300ESB_DEBUG 1*/
                     31: 
                     32: #ifdef I6300ESB_DEBUG
                     33: #define i6300esb_debug(fs,...) \
                     34:     fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
                     35: #else
                     36: #define i6300esb_debug(fs,...)
                     37: #endif
                     38: 
                     39: /* PCI configuration registers */
                     40: #define ESB_CONFIG_REG  0x60            /* Config register                   */
                     41: #define ESB_LOCK_REG    0x68            /* WDT lock register                 */
                     42: 
                     43: /* Memory mapped registers (offset from base address) */
                     44: #define ESB_TIMER1_REG  0x00            /* Timer1 value after each reset     */
                     45: #define ESB_TIMER2_REG  0x04            /* Timer2 value after each reset     */
                     46: #define ESB_GINTSR_REG  0x08            /* General Interrupt Status Register */
                     47: #define ESB_RELOAD_REG  0x0c            /* Reload register                   */
                     48: 
                     49: /* Lock register bits */
                     50: #define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
                     51: #define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
                     52: #define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
                     53: 
                     54: /* Config register bits */
                     55: #define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
                     56: #define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
                     57: #define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
                     58: 
                     59: /* Reload register bits */
                     60: #define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
                     61: 
                     62: /* Magic constants */
                     63: #define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
                     64: #define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
                     65: 
                     66: /* Device state. */
                     67: struct I6300State {
1.1.1.2   root       68:     PCIDevice dev;
1.1       root       69: 
                     70:     int reboot_enabled;         /* "Reboot" on timer expiry.  The real action
                     71:                                  * performed depends on the -watchdog-action
                     72:                                  * param passed on QEMU command line.
                     73:                                  */
                     74:     int clock_scale;            /* Clock scale. */
                     75: #define CLOCK_SCALE_1KHZ 0
                     76: #define CLOCK_SCALE_1MHZ 1
                     77: 
                     78:     int int_type;               /* Interrupt type generated. */
                     79: #define INT_TYPE_IRQ 0          /* APIC 1, INT 10 */
                     80: #define INT_TYPE_SMI 2
                     81: #define INT_TYPE_DISABLED 3
                     82: 
                     83:     int free_run;               /* If true, reload timer on expiry. */
                     84:     int locked;                 /* If true, enabled field cannot be changed. */
                     85:     int enabled;                /* If true, watchdog is enabled. */
                     86: 
                     87:     QEMUTimer *timer;           /* The actual watchdog timer. */
                     88: 
                     89:     uint32_t timer1_preload;    /* Values preloaded into timer1, timer2. */
                     90:     uint32_t timer2_preload;
                     91:     int stage;                  /* Stage (1 or 2). */
                     92: 
                     93:     int unlock_state;           /* Guest writes 0x80, 0x86 to unlock the
                     94:                                  * registers, and we transition through
                     95:                                  * states 0 -> 1 -> 2 when this happens.
                     96:                                  */
                     97: 
                     98:     int previous_reboot_flag;   /* If the watchdog caused the previous
                     99:                                  * reboot, this flag will be set.
                    100:                                  */
                    101: };
                    102: 
                    103: typedef struct I6300State I6300State;
                    104: 
                    105: /* This function is called when the watchdog has either been enabled
                    106:  * (hence it starts counting down) or has been keep-alived.
                    107:  */
                    108: static void i6300esb_restart_timer(I6300State *d, int stage)
                    109: {
                    110:     int64_t timeout;
                    111: 
                    112:     if (!d->enabled)
                    113:         return;
                    114: 
                    115:     d->stage = stage;
                    116: 
                    117:     if (d->stage <= 1)
                    118:         timeout = d->timer1_preload;
                    119:     else
                    120:         timeout = d->timer2_preload;
                    121: 
                    122:     if (d->clock_scale == CLOCK_SCALE_1KHZ)
                    123:         timeout <<= 15;
                    124:     else
                    125:         timeout <<= 5;
                    126: 
                    127:     /* Get the timeout in units of ticks_per_sec. */
1.1.1.2   root      128:     timeout = get_ticks_per_sec() * timeout / 33000000;
1.1       root      129: 
                    130:     i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
                    131: 
                    132:     qemu_mod_timer(d->timer, qemu_get_clock(vm_clock) + timeout);
                    133: }
                    134: 
                    135: /* This is called when the guest disables the watchdog. */
                    136: static void i6300esb_disable_timer(I6300State *d)
                    137: {
                    138:     i6300esb_debug("timer disabled\n");
                    139: 
                    140:     qemu_del_timer(d->timer);
                    141: }
                    142: 
                    143: static void i6300esb_reset(I6300State *d)
                    144: {
                    145:     /* XXX We should probably reset other parts of the state here,
                    146:      * but we should also reset our state on general machine reset
                    147:      * too.  For now just disable the timer so it doesn't fire
                    148:      * again after the reboot.
                    149:      */
                    150:     i6300esb_disable_timer(d);
                    151: }
                    152: 
                    153: /* This function is called when the watchdog expires.  Note that
                    154:  * the hardware has two timers, and so expiry happens in two stages.
                    155:  * If d->stage == 1 then we perform the first stage action (usually,
                    156:  * sending an interrupt) and then restart the timer again for the
                    157:  * second stage.  If the second stage expires then the watchdog
                    158:  * really has run out.
                    159:  */
                    160: static void i6300esb_timer_expired(void *vp)
                    161: {
1.1.1.2   root      162:     I6300State *d = vp;
1.1       root      163: 
                    164:     i6300esb_debug("stage %d\n", d->stage);
                    165: 
                    166:     if (d->stage == 1) {
                    167:         /* What to do at the end of stage 1? */
                    168:         switch (d->int_type) {
                    169:         case INT_TYPE_IRQ:
                    170:             fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
                    171:             break;
                    172:         case INT_TYPE_SMI:
                    173:             fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
                    174:             break;
                    175:         }
                    176: 
                    177:         /* Start the second stage. */
                    178:         i6300esb_restart_timer(d, 2);
                    179:     } else {
                    180:         /* Second stage expired, reboot for real. */
                    181:         if (d->reboot_enabled) {
                    182:             d->previous_reboot_flag = 1;
                    183:             watchdog_perform_action(); /* This reboots, exits, etc */
                    184:             i6300esb_reset(d);
                    185:         }
                    186: 
                    187:         /* In "free running mode" we start stage 1 again. */
                    188:         if (d->free_run)
                    189:             i6300esb_restart_timer(d, 1);
                    190:     }
                    191: }
                    192: 
                    193: static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
                    194:                                   uint32_t data, int len)
                    195: {
1.1.1.2   root      196:     I6300State *d = DO_UPCAST(I6300State, dev, dev);
1.1       root      197:     int old;
                    198: 
                    199:     i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len);
                    200: 
                    201:     if (addr == ESB_CONFIG_REG && len == 2) {
                    202:         d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
                    203:         d->clock_scale =
                    204:             (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
                    205:         d->int_type = (data & ESB_WDT_INTTYPE);
                    206:     } else if (addr == ESB_LOCK_REG && len == 1) {
                    207:         if (!d->locked) {
                    208:             d->locked = (data & ESB_WDT_LOCK) != 0;
                    209:             d->free_run = (data & ESB_WDT_FUNC) != 0;
                    210:             old = d->enabled;
                    211:             d->enabled = (data & ESB_WDT_ENABLE) != 0;
                    212:             if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
                    213:                 i6300esb_restart_timer(d, 1);
                    214:             else if (!d->enabled)
                    215:                 i6300esb_disable_timer(d);
                    216:         }
                    217:     } else {
                    218:         pci_default_write_config(dev, addr, data, len);
                    219:     }
                    220: }
                    221: 
                    222: static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
                    223: {
1.1.1.2   root      224:     I6300State *d = DO_UPCAST(I6300State, dev, dev);
1.1       root      225:     uint32_t data;
                    226: 
                    227:     i6300esb_debug ("addr = %x, len = %d\n", addr, len);
                    228: 
                    229:     if (addr == ESB_CONFIG_REG && len == 2) {
                    230:         data =
                    231:             (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
                    232:             (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
                    233:             d->int_type;
                    234:         return data;
                    235:     } else if (addr == ESB_LOCK_REG && len == 1) {
                    236:         data =
                    237:             (d->free_run ? ESB_WDT_FUNC : 0) |
                    238:             (d->locked ? ESB_WDT_LOCK : 0) |
                    239:             (d->enabled ? ESB_WDT_ENABLE : 0);
                    240:         return data;
                    241:     } else {
                    242:         return pci_default_read_config(dev, addr, len);
                    243:     }
                    244: }
                    245: 
                    246: static uint32_t i6300esb_mem_readb(void *vp, target_phys_addr_t addr)
                    247: {
                    248:     i6300esb_debug ("addr = %x\n", (int) addr);
                    249: 
                    250:     return 0;
                    251: }
                    252: 
                    253: static uint32_t i6300esb_mem_readw(void *vp, target_phys_addr_t addr)
                    254: {
                    255:     uint32_t data = 0;
1.1.1.2   root      256:     I6300State *d = vp;
1.1       root      257: 
                    258:     i6300esb_debug("addr = %x\n", (int) addr);
                    259: 
                    260:     if (addr == 0xc) {
                    261:         /* The previous reboot flag is really bit 9, but there is
                    262:          * a bug in the Linux driver where it thinks it's bit 12.
                    263:          * Set both.
                    264:          */
                    265:         data = d->previous_reboot_flag ? 0x1200 : 0;
                    266:     }
                    267: 
                    268:     return data;
                    269: }
                    270: 
                    271: static uint32_t i6300esb_mem_readl(void *vp, target_phys_addr_t addr)
                    272: {
                    273:     i6300esb_debug("addr = %x\n", (int) addr);
                    274: 
                    275:     return 0;
                    276: }
                    277: 
                    278: static void i6300esb_mem_writeb(void *vp, target_phys_addr_t addr, uint32_t val)
                    279: {
1.1.1.2   root      280:     I6300State *d = vp;
1.1       root      281: 
                    282:     i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
                    283: 
                    284:     if (addr == 0xc && val == 0x80)
                    285:         d->unlock_state = 1;
                    286:     else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
                    287:         d->unlock_state = 2;
                    288: }
                    289: 
                    290: static void i6300esb_mem_writew(void *vp, target_phys_addr_t addr, uint32_t val)
                    291: {
1.1.1.2   root      292:     I6300State *d = vp;
1.1       root      293: 
                    294:     i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
                    295: 
                    296:     if (addr == 0xc && val == 0x80)
                    297:         d->unlock_state = 1;
                    298:     else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
                    299:         d->unlock_state = 2;
                    300:     else {
                    301:         if (d->unlock_state == 2) {
                    302:             if (addr == 0xc) {
                    303:                 if ((val & 0x100) != 0)
                    304:                     /* This is the "ping" from the userspace watchdog in
                    305:                      * the guest ...
                    306:                      */
                    307:                     i6300esb_restart_timer(d, 1);
                    308: 
                    309:                 /* Setting bit 9 resets the previous reboot flag.
                    310:                  * There's a bug in the Linux driver where it sets
                    311:                  * bit 12 instead.
                    312:                  */
                    313:                 if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
                    314:                     d->previous_reboot_flag = 0;
                    315:                 }
                    316:             }
                    317: 
                    318:             d->unlock_state = 0;
                    319:         }
                    320:     }
                    321: }
                    322: 
                    323: static void i6300esb_mem_writel(void *vp, target_phys_addr_t addr, uint32_t val)
                    324: {
1.1.1.2   root      325:     I6300State *d = vp;
1.1       root      326: 
                    327:     i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
                    328: 
                    329:     if (addr == 0xc && val == 0x80)
                    330:         d->unlock_state = 1;
                    331:     else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
                    332:         d->unlock_state = 2;
                    333:     else {
                    334:         if (d->unlock_state == 2) {
                    335:             if (addr == 0)
                    336:                 d->timer1_preload = val & 0xfffff;
                    337:             else if (addr == 4)
                    338:                 d->timer2_preload = val & 0xfffff;
                    339: 
                    340:             d->unlock_state = 0;
                    341:         }
                    342:     }
                    343: }
                    344: 
                    345: static void i6300esb_map(PCIDevice *dev, int region_num,
1.1.1.2   root      346:                          pcibus_t addr, pcibus_t size, int type)
1.1       root      347: {
1.1.1.2   root      348:     static CPUReadMemoryFunc * const mem_read[3] = {
1.1       root      349:         i6300esb_mem_readb,
                    350:         i6300esb_mem_readw,
                    351:         i6300esb_mem_readl,
                    352:     };
1.1.1.2   root      353:     static CPUWriteMemoryFunc * const mem_write[3] = {
1.1       root      354:         i6300esb_mem_writeb,
                    355:         i6300esb_mem_writew,
                    356:         i6300esb_mem_writel,
                    357:     };
1.1.1.2   root      358:     I6300State *d = DO_UPCAST(I6300State, dev, dev);
1.1       root      359:     int io_mem;
                    360: 
1.1.1.2   root      361:     i6300esb_debug("addr = %"FMT_PCIBUS", size = %"FMT_PCIBUS", type = %d\n",
                    362:                    addr, size, type);
1.1       root      363: 
                    364:     io_mem = cpu_register_io_memory(mem_read, mem_write, d);
                    365:     cpu_register_physical_memory (addr, 0x10, io_mem);
                    366:     /* qemu_register_coalesced_mmio (addr, 0x10); ? */
                    367: }
                    368: 
1.1.1.2   root      369: static const VMStateDescription vmstate_i6300esb = {
                    370:     .name = "i6300esb_wdt",
                    371:     .version_id = sizeof(I6300State),
                    372:     .minimum_version_id = sizeof(I6300State),
                    373:     .minimum_version_id_old = sizeof(I6300State),
                    374:     .fields      = (VMStateField []) {
                    375:         VMSTATE_PCI_DEVICE(dev, I6300State),
                    376:         VMSTATE_INT32(reboot_enabled, I6300State),
                    377:         VMSTATE_INT32(clock_scale, I6300State),
                    378:         VMSTATE_INT32(int_type, I6300State),
                    379:         VMSTATE_INT32(free_run, I6300State),
                    380:         VMSTATE_INT32(locked, I6300State),
                    381:         VMSTATE_INT32(enabled, I6300State),
                    382:         VMSTATE_TIMER(timer, I6300State),
                    383:         VMSTATE_UINT32(timer1_preload, I6300State),
                    384:         VMSTATE_UINT32(timer2_preload, I6300State),
                    385:         VMSTATE_INT32(stage, I6300State),
                    386:         VMSTATE_INT32(unlock_state, I6300State),
                    387:         VMSTATE_INT32(previous_reboot_flag, I6300State),
                    388:         VMSTATE_END_OF_LIST()
                    389:     }
                    390: };
1.1       root      391: 
1.1.1.2   root      392: static int i6300esb_init(PCIDevice *dev)
1.1       root      393: {
1.1.1.2   root      394:     I6300State *d = DO_UPCAST(I6300State, dev, dev);
1.1       root      395:     uint8_t *pci_conf;
                    396: 
                    397:     d->reboot_enabled = 1;
                    398:     d->clock_scale = CLOCK_SCALE_1KHZ;
                    399:     d->int_type = INT_TYPE_IRQ;
                    400:     d->free_run = 0;
                    401:     d->locked = 0;
                    402:     d->enabled = 0;
                    403:     d->timer = qemu_new_timer(vm_clock, i6300esb_timer_expired, d);
                    404:     d->timer1_preload = 0xfffff;
                    405:     d->timer2_preload = 0xfffff;
                    406:     d->stage = 1;
                    407:     d->unlock_state = 0;
                    408:     d->previous_reboot_flag = 0;
                    409: 
                    410:     pci_conf = d->dev.config;
                    411:     pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
                    412:     pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
                    413:     pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER);
                    414: 
                    415:     pci_register_bar(&d->dev, 0, 0x10,
1.1.1.2   root      416:                             PCI_BASE_ADDRESS_SPACE_MEMORY, i6300esb_map);
1.1       root      417: 
1.1.1.2   root      418:     return 0;
1.1       root      419: }
                    420: 
                    421: static WatchdogTimerModel model = {
                    422:     .wdt_name = "i6300esb",
                    423:     .wdt_description = "Intel 6300ESB",
                    424: };
                    425: 
1.1.1.2   root      426: static PCIDeviceInfo i6300esb_info = {
                    427:     .qdev.name    = "i6300esb",
                    428:     .qdev.size    = sizeof(I6300State),
                    429:     .qdev.vmsd    = &vmstate_i6300esb,
                    430:     .config_read  = i6300esb_config_read,
                    431:     .config_write = i6300esb_config_write,
                    432:     .init         = i6300esb_init,
                    433: };
                    434: 
                    435: static void i6300esb_register_devices(void)
1.1       root      436: {
                    437:     watchdog_add_model(&model);
1.1.1.2   root      438:     pci_qdev_register(&i6300esb_info);
1.1       root      439: }
1.1.1.2   root      440: 
                    441: device_init(i6300esb_register_devices);

unix.superglobalmegacorp.com

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