qemu/tests/rtc-test.c
<<
>>
Prefs
   1/*
   2 * QTest testcase for the MC146818 real-time clock
   3 *
   4 * Copyright IBM, Corp. 2012
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 *
  12 */
  13
  14#include "qemu/osdep.h"
  15
  16#include "libqtest.h"
  17#include "hw/timer/mc146818rtc_regs.h"
  18
  19static uint8_t base = 0x70;
  20
  21static int bcd2dec(int value)
  22{
  23    return (((value >> 4) & 0x0F) * 10) + (value & 0x0F);
  24}
  25
  26static uint8_t cmos_read(uint8_t reg)
  27{
  28    outb(base + 0, reg);
  29    return inb(base + 1);
  30}
  31
  32static void cmos_write(uint8_t reg, uint8_t val)
  33{
  34    outb(base + 0, reg);
  35    outb(base + 1, val);
  36}
  37
  38static int tm_cmp(struct tm *lhs, struct tm *rhs)
  39{
  40    time_t a, b;
  41    struct tm d1, d2;
  42
  43    memcpy(&d1, lhs, sizeof(d1));
  44    memcpy(&d2, rhs, sizeof(d2));
  45
  46    a = mktime(&d1);
  47    b = mktime(&d2);
  48
  49    if (a < b) {
  50        return -1;
  51    } else if (a > b) {
  52        return 1;
  53    }
  54
  55    return 0;
  56}
  57
  58#if 0
  59static void print_tm(struct tm *tm)
  60{
  61    printf("%04d-%02d-%02d %02d:%02d:%02d\n",
  62           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  63           tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff);
  64}
  65#endif
  66
  67static void cmos_get_date_time(struct tm *date)
  68{
  69    int base_year = 2000, hour_offset;
  70    int sec, min, hour, mday, mon, year;
  71    time_t ts;
  72    struct tm dummy;
  73
  74    sec = cmos_read(RTC_SECONDS);
  75    min = cmos_read(RTC_MINUTES);
  76    hour = cmos_read(RTC_HOURS);
  77    mday = cmos_read(RTC_DAY_OF_MONTH);
  78    mon = cmos_read(RTC_MONTH);
  79    year = cmos_read(RTC_YEAR);
  80
  81    if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) {
  82        sec = bcd2dec(sec);
  83        min = bcd2dec(min);
  84        hour = bcd2dec(hour);
  85        mday = bcd2dec(mday);
  86        mon = bcd2dec(mon);
  87        year = bcd2dec(year);
  88        hour_offset = 80;
  89    } else {
  90        hour_offset = 0x80;
  91    }
  92
  93    if ((cmos_read(0x0B) & REG_B_24H) == 0) {
  94        if (hour >= hour_offset) {
  95            hour -= hour_offset;
  96            hour += 12;
  97        }
  98    }
  99
 100    ts = time(NULL);
 101    localtime_r(&ts, &dummy);
 102
 103    date->tm_isdst = dummy.tm_isdst;
 104    date->tm_sec = sec;
 105    date->tm_min = min;
 106    date->tm_hour = hour;
 107    date->tm_mday = mday;
 108    date->tm_mon = mon - 1;
 109    date->tm_year = base_year + year - 1900;
 110#ifndef __sun__
 111    date->tm_gmtoff = 0;
 112#endif
 113
 114    ts = mktime(date);
 115}
 116
 117static void check_time(int wiggle)
 118{
 119    struct tm start, date[4], end;
 120    struct tm *datep;
 121    time_t ts;
 122
 123    /*
 124     * This check assumes a few things.  First, we cannot guarantee that we get
 125     * a consistent reading from the wall clock because we may hit an edge of
 126     * the clock while reading.  To work around this, we read four clock readings
 127     * such that at least two of them should match.  We need to assume that one
 128     * reading is corrupt so we need four readings to ensure that we have at
 129     * least two consecutive identical readings
 130     *
 131     * It's also possible that we'll cross an edge reading the host clock so
 132     * simply check to make sure that the clock reading is within the period of
 133     * when we expect it to be.
 134     */
 135
 136    ts = time(NULL);
 137    gmtime_r(&ts, &start);
 138
 139    cmos_get_date_time(&date[0]);
 140    cmos_get_date_time(&date[1]);
 141    cmos_get_date_time(&date[2]);
 142    cmos_get_date_time(&date[3]);
 143
 144    ts = time(NULL);
 145    gmtime_r(&ts, &end);
 146
 147    if (tm_cmp(&date[0], &date[1]) == 0) {
 148        datep = &date[0];
 149    } else if (tm_cmp(&date[1], &date[2]) == 0) {
 150        datep = &date[1];
 151    } else if (tm_cmp(&date[2], &date[3]) == 0) {
 152        datep = &date[2];
 153    } else {
 154        g_assert_not_reached();
 155    }
 156
 157    if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
 158        long t, s;
 159
 160        start.tm_isdst = datep->tm_isdst;
 161
 162        t = (long)mktime(datep);
 163        s = (long)mktime(&start);
 164        if (t < s) {
 165            g_test_message("RTC is %ld second(s) behind wall-clock\n", (s - t));
 166        } else {
 167            g_test_message("RTC is %ld second(s) ahead of wall-clock\n", (t - s));
 168        }
 169
 170        g_assert_cmpint(ABS(t - s), <=, wiggle);
 171    }
 172}
 173
 174static int wiggle = 2;
 175
 176static void set_year_20xx(void)
 177{
 178    /* Set BCD mode */
 179    cmos_write(RTC_REG_B, REG_B_24H);
 180    cmos_write(RTC_REG_A, 0x76);
 181    cmos_write(RTC_YEAR, 0x11);
 182    cmos_write(RTC_CENTURY, 0x20);
 183    cmos_write(RTC_MONTH, 0x02);
 184    cmos_write(RTC_DAY_OF_MONTH, 0x02);
 185    cmos_write(RTC_HOURS, 0x02);
 186    cmos_write(RTC_MINUTES, 0x04);
 187    cmos_write(RTC_SECONDS, 0x58);
 188    cmos_write(RTC_REG_A, 0x26);
 189
 190    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
 191    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
 192    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
 193    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
 194    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
 195    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
 196    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
 197
 198    if (sizeof(time_t) == 4) {
 199        return;
 200    }
 201
 202    /* Set a date in 2080 to ensure there is no year-2038 overflow.  */
 203    cmos_write(RTC_REG_A, 0x76);
 204    cmos_write(RTC_YEAR, 0x80);
 205    cmos_write(RTC_REG_A, 0x26);
 206
 207    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
 208    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
 209    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
 210    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
 211    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
 212    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80);
 213    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
 214
 215    cmos_write(RTC_REG_A, 0x76);
 216    cmos_write(RTC_YEAR, 0x11);
 217    cmos_write(RTC_REG_A, 0x26);
 218
 219    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
 220    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
 221    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
 222    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
 223    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
 224    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
 225    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
 226}
 227
 228static void set_year_1980(void)
 229{
 230    /* Set BCD mode */
 231    cmos_write(RTC_REG_B, REG_B_24H);
 232    cmos_write(RTC_REG_A, 0x76);
 233    cmos_write(RTC_YEAR, 0x80);
 234    cmos_write(RTC_CENTURY, 0x19);
 235    cmos_write(RTC_MONTH, 0x02);
 236    cmos_write(RTC_DAY_OF_MONTH, 0x02);
 237    cmos_write(RTC_HOURS, 0x02);
 238    cmos_write(RTC_MINUTES, 0x04);
 239    cmos_write(RTC_SECONDS, 0x58);
 240    cmos_write(RTC_REG_A, 0x26);
 241
 242    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
 243    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
 244    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
 245    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
 246    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
 247    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80);
 248    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x19);
 249}
 250
 251static void bcd_check_time(void)
 252{
 253    /* Set BCD mode */
 254    cmos_write(RTC_REG_B, REG_B_24H);
 255    check_time(wiggle);
 256}
 257
 258static void dec_check_time(void)
 259{
 260    /* Set DEC mode */
 261    cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM);
 262    check_time(wiggle);
 263}
 264
 265static void alarm_time(void)
 266{
 267    struct tm now;
 268    time_t ts;
 269    int i;
 270
 271    ts = time(NULL);
 272    gmtime_r(&ts, &now);
 273
 274    /* set DEC mode */
 275    cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM);
 276
 277    g_assert(!get_irq(RTC_ISA_IRQ));
 278    cmos_read(RTC_REG_C);
 279
 280    now.tm_sec = (now.tm_sec + 2) % 60;
 281    cmos_write(RTC_SECONDS_ALARM, now.tm_sec);
 282    cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
 283    cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
 284    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE);
 285
 286    for (i = 0; i < 2 + wiggle; i++) {
 287        if (get_irq(RTC_ISA_IRQ)) {
 288            break;
 289        }
 290
 291        clock_step(1000000000);
 292    }
 293
 294    g_assert(get_irq(RTC_ISA_IRQ));
 295    g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
 296    g_assert(cmos_read(RTC_REG_C) == 0);
 297}
 298
 299static void set_time(int mode, int h, int m, int s)
 300{
 301    /* set BCD 12 hour mode */
 302    cmos_write(RTC_REG_B, mode);
 303
 304    cmos_write(RTC_REG_A, 0x76);
 305    cmos_write(RTC_HOURS, h);
 306    cmos_write(RTC_MINUTES, m);
 307    cmos_write(RTC_SECONDS, s);
 308    cmos_write(RTC_REG_A, 0x26);
 309}
 310
 311#define assert_time(h, m, s) \
 312    do { \
 313        g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \
 314        g_assert_cmpint(cmos_read(RTC_MINUTES), ==, m); \
 315        g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \
 316    } while(0)
 317
 318static void basic_12h_bcd(void)
 319{
 320    /* set BCD 12 hour mode */
 321    set_time(0, 0x81, 0x59, 0x00);
 322    clock_step(1000000000LL);
 323    assert_time(0x81, 0x59, 0x01);
 324    clock_step(59000000000LL);
 325    assert_time(0x82, 0x00, 0x00);
 326
 327    /* test BCD wraparound */
 328    set_time(0, 0x09, 0x59, 0x59);
 329    clock_step(60000000000LL);
 330    assert_time(0x10, 0x00, 0x59);
 331
 332    /* 12 AM -> 1 AM */
 333    set_time(0, 0x12, 0x59, 0x59);
 334    clock_step(1000000000LL);
 335    assert_time(0x01, 0x00, 0x00);
 336
 337    /* 12 PM -> 1 PM */
 338    set_time(0, 0x92, 0x59, 0x59);
 339    clock_step(1000000000LL);
 340    assert_time(0x81, 0x00, 0x00);
 341
 342    /* 11 AM -> 12 PM */
 343    set_time(0, 0x11, 0x59, 0x59);
 344    clock_step(1000000000LL);
 345    assert_time(0x92, 0x00, 0x00);
 346    /* TODO: test day wraparound */
 347
 348    /* 11 PM -> 12 AM */
 349    set_time(0, 0x91, 0x59, 0x59);
 350    clock_step(1000000000LL);
 351    assert_time(0x12, 0x00, 0x00);
 352    /* TODO: test day wraparound */
 353}
 354
 355static void basic_12h_dec(void)
 356{
 357    /* set decimal 12 hour mode */
 358    set_time(REG_B_DM, 0x81, 59, 0);
 359    clock_step(1000000000LL);
 360    assert_time(0x81, 59, 1);
 361    clock_step(59000000000LL);
 362    assert_time(0x82, 0, 0);
 363
 364    /* 12 PM -> 1 PM */
 365    set_time(REG_B_DM, 0x8c, 59, 59);
 366    clock_step(1000000000LL);
 367    assert_time(0x81, 0, 0);
 368
 369    /* 12 AM -> 1 AM */
 370    set_time(REG_B_DM, 0x0c, 59, 59);
 371    clock_step(1000000000LL);
 372    assert_time(0x01, 0, 0);
 373
 374    /* 11 AM -> 12 PM */
 375    set_time(REG_B_DM, 0x0b, 59, 59);
 376    clock_step(1000000000LL);
 377    assert_time(0x8c, 0, 0);
 378
 379    /* 11 PM -> 12 AM */
 380    set_time(REG_B_DM, 0x8b, 59, 59);
 381    clock_step(1000000000LL);
 382    assert_time(0x0c, 0, 0);
 383    /* TODO: test day wraparound */
 384}
 385
 386static void basic_24h_bcd(void)
 387{
 388    /* set BCD 24 hour mode */
 389    set_time(REG_B_24H, 0x09, 0x59, 0x00);
 390    clock_step(1000000000LL);
 391    assert_time(0x09, 0x59, 0x01);
 392    clock_step(59000000000LL);
 393    assert_time(0x10, 0x00, 0x00);
 394
 395    /* test BCD wraparound */
 396    set_time(REG_B_24H, 0x09, 0x59, 0x00);
 397    clock_step(60000000000LL);
 398    assert_time(0x10, 0x00, 0x00);
 399
 400    /* TODO: test day wraparound */
 401    set_time(REG_B_24H, 0x23, 0x59, 0x00);
 402    clock_step(60000000000LL);
 403    assert_time(0x00, 0x00, 0x00);
 404}
 405
 406static void basic_24h_dec(void)
 407{
 408    /* set decimal 24 hour mode */
 409    set_time(REG_B_24H | REG_B_DM, 9, 59, 0);
 410    clock_step(1000000000LL);
 411    assert_time(9, 59, 1);
 412    clock_step(59000000000LL);
 413    assert_time(10, 0, 0);
 414
 415    /* test BCD wraparound */
 416    set_time(REG_B_24H | REG_B_DM, 9, 59, 0);
 417    clock_step(60000000000LL);
 418    assert_time(10, 0, 0);
 419
 420    /* TODO: test day wraparound */
 421    set_time(REG_B_24H | REG_B_DM, 23, 59, 0);
 422    clock_step(60000000000LL);
 423    assert_time(0, 0, 0);
 424}
 425
 426static void am_pm_alarm(void)
 427{
 428    cmos_write(RTC_MINUTES_ALARM, 0xC0);
 429    cmos_write(RTC_SECONDS_ALARM, 0xC0);
 430
 431    /* set BCD 12 hour mode */
 432    cmos_write(RTC_REG_B, 0);
 433
 434    /* Set time and alarm hour.  */
 435    cmos_write(RTC_REG_A, 0x76);
 436    cmos_write(RTC_HOURS_ALARM, 0x82);
 437    cmos_write(RTC_HOURS, 0x81);
 438    cmos_write(RTC_MINUTES, 0x59);
 439    cmos_write(RTC_SECONDS, 0x00);
 440    cmos_read(RTC_REG_C);
 441    cmos_write(RTC_REG_A, 0x26);
 442
 443    /* Check that alarm triggers when AM/PM is set.  */
 444    clock_step(60000000000LL);
 445    g_assert(cmos_read(RTC_HOURS) == 0x82);
 446    g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
 447
 448    /*
 449     * Each of the following two tests takes over 60 seconds due to the time
 450     * needed to report the PIT interrupts.  Unfortunately, our PIT device
 451     * model keeps counting even when GATE=0, so we cannot simply disable
 452     * it in main().
 453     */
 454    if (g_test_quick()) {
 455        return;
 456    }
 457
 458    /* set DEC 12 hour mode */
 459    cmos_write(RTC_REG_B, REG_B_DM);
 460
 461    /* Set time and alarm hour.  */
 462    cmos_write(RTC_REG_A, 0x76);
 463    cmos_write(RTC_HOURS_ALARM, 0x82);
 464    cmos_write(RTC_HOURS, 3);
 465    cmos_write(RTC_MINUTES, 0);
 466    cmos_write(RTC_SECONDS, 0);
 467    cmos_read(RTC_REG_C);
 468    cmos_write(RTC_REG_A, 0x26);
 469
 470    /* Check that alarm triggers.  */
 471    clock_step(3600 * 11 * 1000000000LL);
 472    g_assert(cmos_read(RTC_HOURS) == 0x82);
 473    g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
 474
 475    /* Same as above, with inverted HOURS and HOURS_ALARM.  */
 476    cmos_write(RTC_REG_A, 0x76);
 477    cmos_write(RTC_HOURS_ALARM, 2);
 478    cmos_write(RTC_HOURS, 3);
 479    cmos_write(RTC_MINUTES, 0);
 480    cmos_write(RTC_SECONDS, 0);
 481    cmos_read(RTC_REG_C);
 482    cmos_write(RTC_REG_A, 0x26);
 483
 484    /* Check that alarm does not trigger if hours differ only by AM/PM.  */
 485    clock_step(3600 * 11 * 1000000000LL);
 486    g_assert(cmos_read(RTC_HOURS) == 0x82);
 487    g_assert((cmos_read(RTC_REG_C) & REG_C_AF) == 0);
 488}
 489
 490/* success if no crash or abort */
 491static void fuzz_registers(void)
 492{
 493    unsigned int i;
 494
 495    for (i = 0; i < 1000; i++) {
 496        uint8_t reg, val;
 497
 498        reg = (uint8_t)g_test_rand_int_range(0, 16);
 499        val = (uint8_t)g_test_rand_int_range(0, 256);
 500
 501        cmos_write(reg, val);
 502        cmos_read(reg);
 503    }
 504}
 505
 506static void register_b_set_flag(void)
 507{
 508    /* Enable binary-coded decimal (BCD) mode and SET flag in Register B*/
 509    cmos_write(RTC_REG_B, REG_B_24H | REG_B_SET);
 510
 511    cmos_write(RTC_REG_A, 0x76);
 512    cmos_write(RTC_YEAR, 0x11);
 513    cmos_write(RTC_CENTURY, 0x20);
 514    cmos_write(RTC_MONTH, 0x02);
 515    cmos_write(RTC_DAY_OF_MONTH, 0x02);
 516    cmos_write(RTC_HOURS, 0x02);
 517    cmos_write(RTC_MINUTES, 0x04);
 518    cmos_write(RTC_SECONDS, 0x58);
 519    cmos_write(RTC_REG_A, 0x26);
 520
 521    /* Since SET flag is still enabled, these are equality checks. */
 522    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
 523    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
 524    g_assert_cmpint(cmos_read(RTC_SECONDS), ==, 0x58);
 525    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
 526    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
 527    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
 528    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
 529
 530    /* Disable SET flag in Register B */
 531    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_SET);
 532
 533    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
 534    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
 535
 536    /* Since SET flag is disabled, this is an inequality check.
 537     * We (reasonably) assume that no (sexagesimal) overflow occurs. */
 538    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
 539    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
 540    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
 541    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
 542    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
 543}
 544
 545int main(int argc, char **argv)
 546{
 547    QTestState *s = NULL;
 548    int ret;
 549
 550    g_test_init(&argc, &argv, NULL);
 551
 552    s = qtest_start("-rtc clock=vm");
 553    qtest_irq_intercept_in(s, "ioapic");
 554
 555    qtest_add_func("/rtc/check-time/bcd", bcd_check_time);
 556    qtest_add_func("/rtc/check-time/dec", dec_check_time);
 557    qtest_add_func("/rtc/alarm/interrupt", alarm_time);
 558    qtest_add_func("/rtc/alarm/am-pm", am_pm_alarm);
 559    qtest_add_func("/rtc/basic/dec-24h", basic_24h_dec);
 560    qtest_add_func("/rtc/basic/bcd-24h", basic_24h_bcd);
 561    qtest_add_func("/rtc/basic/dec-12h", basic_12h_dec);
 562    qtest_add_func("/rtc/basic/bcd-12h", basic_12h_bcd);
 563    qtest_add_func("/rtc/set-year/20xx", set_year_20xx);
 564    qtest_add_func("/rtc/set-year/1980", set_year_1980);
 565    qtest_add_func("/rtc/misc/register_b_set_flag", register_b_set_flag);
 566    qtest_add_func("/rtc/misc/fuzz-registers", fuzz_registers);
 567    ret = g_test_run();
 568
 569    if (s) {
 570        qtest_quit(s);
 571    }
 572
 573    return ret;
 574}
 575