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