qemu/tests/tco-test.c
<<
>>
Prefs
   1/*
   2 * QEMU ICH9 TCO emulation tests
   3 *
   4 * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 */
   9#include "qemu/osdep.h"
  10#include <glib.h>
  11
  12#include "libqtest.h"
  13#include "libqos/pci.h"
  14#include "libqos/pci-pc.h"
  15#include "hw/pci/pci_regs.h"
  16#include "hw/i386/ich9.h"
  17#include "hw/acpi/ich9.h"
  18#include "hw/acpi/tco.h"
  19
  20#define RCBA_BASE_ADDR    0xfed1c000
  21#define PM_IO_BASE_ADDR   0xb000
  22
  23enum {
  24    TCO_RLD_DEFAULT         = 0x0000,
  25    TCO_DAT_IN_DEFAULT      = 0x00,
  26    TCO_DAT_OUT_DEFAULT     = 0x00,
  27    TCO1_STS_DEFAULT        = 0x0000,
  28    TCO2_STS_DEFAULT        = 0x0000,
  29    TCO1_CNT_DEFAULT        = 0x0000,
  30    TCO2_CNT_DEFAULT        = 0x0008,
  31    TCO_MESSAGE1_DEFAULT    = 0x00,
  32    TCO_MESSAGE2_DEFAULT    = 0x00,
  33    TCO_WDCNT_DEFAULT       = 0x00,
  34    TCO_TMR_DEFAULT         = 0x0004,
  35    SW_IRQ_GEN_DEFAULT      = 0x03,
  36};
  37
  38#define TCO_SECS_TO_TICKS(secs)     (((secs) * 10) / 6)
  39#define TCO_TICKS_TO_SECS(ticks)    (((ticks) * 6) / 10)
  40
  41typedef struct {
  42    const char *args;
  43    bool noreboot;
  44    QPCIDevice *dev;
  45    void *tco_io_base;
  46} TestData;
  47
  48static void test_init(TestData *d)
  49{
  50    QPCIBus *bus;
  51    QTestState *qs;
  52    char *s;
  53
  54    s = g_strdup_printf("-machine q35 %s %s",
  55                        d->noreboot ? "" : "-global ICH9-LPC.noreboot=false",
  56                        !d->args ? "" : d->args);
  57    qs = qtest_start(s);
  58    qtest_irq_intercept_in(qs, "ioapic");
  59    g_free(s);
  60
  61    bus = qpci_init_pc();
  62    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
  63    g_assert(d->dev != NULL);
  64
  65    qpci_device_enable(d->dev);
  66
  67    /* set ACPI PM I/O space base address */
  68    qpci_config_writel(d->dev, ICH9_LPC_PMBASE, PM_IO_BASE_ADDR | 0x1);
  69    /* enable ACPI I/O */
  70    qpci_config_writeb(d->dev, ICH9_LPC_ACPI_CTRL, 0x80);
  71    /* set Root Complex BAR */
  72    qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1);
  73
  74    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
  75}
  76
  77static void stop_tco(const TestData *d)
  78{
  79    uint32_t val;
  80
  81    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
  82    val |= TCO_TMR_HLT;
  83    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
  84}
  85
  86static void start_tco(const TestData *d)
  87{
  88    uint32_t val;
  89
  90    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
  91    val &= ~TCO_TMR_HLT;
  92    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
  93}
  94
  95static void load_tco(const TestData *d)
  96{
  97    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
  98}
  99
 100static void set_tco_timeout(const TestData *d, uint16_t ticks)
 101{
 102    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
 103}
 104
 105static void clear_tco_status(const TestData *d)
 106{
 107    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
 108    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
 109    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
 110}
 111
 112static void reset_on_second_timeout(bool enable)
 113{
 114    uint32_t val;
 115
 116    val = readl(RCBA_BASE_ADDR + ICH9_CC_GCS);
 117    if (enable) {
 118        val &= ~ICH9_CC_GCS_NO_REBOOT;
 119    } else {
 120        val |= ICH9_CC_GCS_NO_REBOOT;
 121    }
 122    writel(RCBA_BASE_ADDR + ICH9_CC_GCS, val);
 123}
 124
 125static void test_tco_defaults(void)
 126{
 127    TestData d;
 128
 129    d.args = NULL;
 130    d.noreboot = true;
 131    test_init(&d);
 132    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
 133                    TCO_RLD_DEFAULT);
 134    /* TCO_DAT_IN & TCO_DAT_OUT */
 135    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
 136                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
 137    /* TCO1_STS & TCO2_STS */
 138    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
 139                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
 140    /* TCO1_CNT & TCO2_CNT */
 141    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
 142                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
 143    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
 144    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
 145                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
 146    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
 147                    TCO_WDCNT_DEFAULT);
 148    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
 149                    SW_IRQ_GEN_DEFAULT);
 150    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
 151                    TCO_TMR_DEFAULT);
 152    qtest_end();
 153}
 154
 155static void test_tco_timeout(void)
 156{
 157    TestData d;
 158    const uint16_t ticks = TCO_SECS_TO_TICKS(4);
 159    uint32_t val;
 160    int ret;
 161
 162    d.args = NULL;
 163    d.noreboot = true;
 164    test_init(&d);
 165
 166    stop_tco(&d);
 167    clear_tco_status(&d);
 168    reset_on_second_timeout(false);
 169    set_tco_timeout(&d, ticks);
 170    load_tco(&d);
 171    start_tco(&d);
 172    clock_step(ticks * TCO_TICK_NSEC);
 173
 174    /* test first timeout */
 175    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
 176    ret = val & TCO_TIMEOUT ? 1 : 0;
 177    g_assert(ret == 1);
 178
 179    /* test clearing timeout bit */
 180    val |= TCO_TIMEOUT;
 181    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
 182    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
 183    ret = val & TCO_TIMEOUT ? 1 : 0;
 184    g_assert(ret == 0);
 185
 186    /* test second timeout */
 187    clock_step(ticks * TCO_TICK_NSEC);
 188    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
 189    ret = val & TCO_TIMEOUT ? 1 : 0;
 190    g_assert(ret == 1);
 191    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
 192    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
 193    g_assert(ret == 1);
 194
 195    stop_tco(&d);
 196    qtest_end();
 197}
 198
 199static void test_tco_max_timeout(void)
 200{
 201    TestData d;
 202    const uint16_t ticks = 0xffff;
 203    uint32_t val;
 204    int ret;
 205
 206    d.args = NULL;
 207    d.noreboot = true;
 208    test_init(&d);
 209
 210    stop_tco(&d);
 211    clear_tco_status(&d);
 212    reset_on_second_timeout(false);
 213    set_tco_timeout(&d, ticks);
 214    load_tco(&d);
 215    start_tco(&d);
 216    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
 217
 218    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
 219    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
 220    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
 221    ret = val & TCO_TIMEOUT ? 1 : 0;
 222    g_assert(ret == 0);
 223    clock_step(TCO_TICK_NSEC);
 224    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
 225    ret = val & TCO_TIMEOUT ? 1 : 0;
 226    g_assert(ret == 1);
 227
 228    stop_tco(&d);
 229    qtest_end();
 230}
 231
 232static QDict *get_watchdog_action(void)
 233{
 234    QDict *ev = qmp("");
 235    QDict *data;
 236    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
 237
 238    data = qdict_get_qdict(ev, "data");
 239    QINCREF(data);
 240    QDECREF(ev);
 241    return data;
 242}
 243
 244static void test_tco_second_timeout_pause(void)
 245{
 246    TestData td;
 247    const uint16_t ticks = TCO_SECS_TO_TICKS(32);
 248    QDict *ad;
 249
 250    td.args = "-watchdog-action pause";
 251    td.noreboot = false;
 252    test_init(&td);
 253
 254    stop_tco(&td);
 255    clear_tco_status(&td);
 256    reset_on_second_timeout(true);
 257    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
 258    load_tco(&td);
 259    start_tco(&td);
 260    clock_step(ticks * TCO_TICK_NSEC * 2);
 261    ad = get_watchdog_action();
 262    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
 263    QDECREF(ad);
 264
 265    stop_tco(&td);
 266    qtest_end();
 267}
 268
 269static void test_tco_second_timeout_reset(void)
 270{
 271    TestData td;
 272    const uint16_t ticks = TCO_SECS_TO_TICKS(16);
 273    QDict *ad;
 274
 275    td.args = "-watchdog-action reset";
 276    td.noreboot = false;
 277    test_init(&td);
 278
 279    stop_tco(&td);
 280    clear_tco_status(&td);
 281    reset_on_second_timeout(true);
 282    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
 283    load_tco(&td);
 284    start_tco(&td);
 285    clock_step(ticks * TCO_TICK_NSEC * 2);
 286    ad = get_watchdog_action();
 287    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
 288    QDECREF(ad);
 289
 290    stop_tco(&td);
 291    qtest_end();
 292}
 293
 294static void test_tco_second_timeout_shutdown(void)
 295{
 296    TestData td;
 297    const uint16_t ticks = TCO_SECS_TO_TICKS(128);
 298    QDict *ad;
 299
 300    td.args = "-watchdog-action shutdown";
 301    td.noreboot = false;
 302    test_init(&td);
 303
 304    stop_tco(&td);
 305    clear_tco_status(&td);
 306    reset_on_second_timeout(true);
 307    set_tco_timeout(&td, ticks);
 308    load_tco(&td);
 309    start_tco(&td);
 310    clock_step(ticks * TCO_TICK_NSEC * 2);
 311    ad = get_watchdog_action();
 312    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
 313    QDECREF(ad);
 314
 315    stop_tco(&td);
 316    qtest_end();
 317}
 318
 319static void test_tco_second_timeout_none(void)
 320{
 321    TestData td;
 322    const uint16_t ticks = TCO_SECS_TO_TICKS(256);
 323    QDict *ad;
 324
 325    td.args = "-watchdog-action none";
 326    td.noreboot = false;
 327    test_init(&td);
 328
 329    stop_tco(&td);
 330    clear_tco_status(&td);
 331    reset_on_second_timeout(true);
 332    set_tco_timeout(&td, ticks);
 333    load_tco(&td);
 334    start_tco(&td);
 335    clock_step(ticks * TCO_TICK_NSEC * 2);
 336    ad = get_watchdog_action();
 337    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
 338    QDECREF(ad);
 339
 340    stop_tco(&td);
 341    qtest_end();
 342}
 343
 344static void test_tco_ticks_counter(void)
 345{
 346    TestData d;
 347    uint16_t ticks = TCO_SECS_TO_TICKS(8);
 348    uint16_t rld;
 349
 350    d.args = NULL;
 351    d.noreboot = true;
 352    test_init(&d);
 353
 354    stop_tco(&d);
 355    clear_tco_status(&d);
 356    reset_on_second_timeout(false);
 357    set_tco_timeout(&d, ticks);
 358    load_tco(&d);
 359    start_tco(&d);
 360
 361    do {
 362        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
 363        g_assert_cmpint(rld, ==, ticks);
 364        clock_step(TCO_TICK_NSEC);
 365        ticks--;
 366    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
 367
 368    stop_tco(&d);
 369    qtest_end();
 370}
 371
 372static void test_tco1_control_bits(void)
 373{
 374    TestData d;
 375    uint16_t val;
 376
 377    d.args = NULL;
 378    d.noreboot = true;
 379    test_init(&d);
 380
 381    val = TCO_LOCK;
 382    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
 383    val &= ~TCO_LOCK;
 384    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
 385    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
 386                    TCO_LOCK);
 387    qtest_end();
 388}
 389
 390static void test_tco1_status_bits(void)
 391{
 392    TestData d;
 393    uint16_t ticks = 8;
 394    uint16_t val;
 395    int ret;
 396
 397    d.args = NULL;
 398    d.noreboot = true;
 399    test_init(&d);
 400
 401    stop_tco(&d);
 402    clear_tco_status(&d);
 403    reset_on_second_timeout(false);
 404    set_tco_timeout(&d, ticks);
 405    load_tco(&d);
 406    start_tco(&d);
 407    clock_step(ticks * TCO_TICK_NSEC);
 408
 409    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
 410    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
 411    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
 412    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
 413    g_assert(ret == 1);
 414    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
 415    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
 416    qtest_end();
 417}
 418
 419static void test_tco2_status_bits(void)
 420{
 421    TestData d;
 422    uint16_t ticks = 8;
 423    uint16_t val;
 424    int ret;
 425
 426    d.args = NULL;
 427    d.noreboot = true;
 428    test_init(&d);
 429
 430    stop_tco(&d);
 431    clear_tco_status(&d);
 432    reset_on_second_timeout(true);
 433    set_tco_timeout(&d, ticks);
 434    load_tco(&d);
 435    start_tco(&d);
 436    clock_step(ticks * TCO_TICK_NSEC * 2);
 437
 438    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
 439    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
 440    g_assert(ret == 1);
 441    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
 442    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
 443    qtest_end();
 444}
 445
 446int main(int argc, char **argv)
 447{
 448    g_test_init(&argc, &argv, NULL);
 449
 450    qtest_add_func("tco/defaults", test_tco_defaults);
 451    qtest_add_func("tco/timeout/no_action", test_tco_timeout);
 452    qtest_add_func("tco/timeout/no_action/max", test_tco_max_timeout);
 453    qtest_add_func("tco/second_timeout/pause", test_tco_second_timeout_pause);
 454    qtest_add_func("tco/second_timeout/reset", test_tco_second_timeout_reset);
 455    qtest_add_func("tco/second_timeout/shutdown",
 456                   test_tco_second_timeout_shutdown);
 457    qtest_add_func("tco/second_timeout/none", test_tco_second_timeout_none);
 458    qtest_add_func("tco/counter", test_tco_ticks_counter);
 459    qtest_add_func("tco/tco1_control/bits", test_tco1_control_bits);
 460    qtest_add_func("tco/tco1_status/bits", test_tco1_status_bits);
 461    qtest_add_func("tco/tco2_status/bits", test_tco2_status_bits);
 462    return g_test_run();
 463}
 464