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