Patchwork [3/3] qtest: add m48t59 tests for Sparc

login
register
mail settings
Submitter Blue Swirl
Date April 15, 2012, 4:32 p.m.
Message ID <CAAu8pHsbTENN=wEWLQM-m4Po_1+YtR++j=82kwotVj=7w2gyYQ@mail.gmail.com>
Download mbox | patch
Permalink /patch/152662/
State New
Headers show

Comments

Blue Swirl - April 15, 2012, 4:32 p.m.
Add simple m48t59 qtests, enable test only for Sparc32
and Sparc64. On PPC, the device is behind PCI bus.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
---
 tests/Makefile      |    5 +
 tests/m48t59-test.c |  259 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 264 insertions(+)
 create mode 100644 tests/m48t59-test.c

+
+    return ret;
+}
Andreas Färber - April 16, 2012, 1:01 p.m.
Am 15.04.2012 18:32, schrieb Blue Swirl:
> Add simple m48t59 qtests, enable test only for Sparc32
> and Sparc64. On PPC, the device is behind PCI bus.
> 
> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
> ---
>  tests/Makefile      |    5 +
>  tests/m48t59-test.c |  259 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 264 insertions(+)
>  create mode 100644 tests/m48t59-test.c
> 
> diff --git a/tests/Makefile b/tests/Makefile
> index baf1d70..04a8050 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -20,6 +20,10 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
>  # really in libqtest, not in the testcases themselves.
>  check-qtest-i386-y = tests/rtc-test
>  check-qtest-x86_64-y = $(check-qtest-i386-y)
> +#check-qtest-ppc-y = tests/m48t59-test
> +#check-qtest-ppc64-y = tests/m48t59-test
> +check-qtest-sparc-y = tests/m48t59-test
> +check-qtest-sparc64-y = tests/m48t59-test

For Stefan not to hang you, you should probably add $(EXESUF). ;)

Some way of not duplicating the test four times would be nice. Maybe
$(check-qtest-m48t59-y) or at least $(check-qtest-sparc-y) for sparc64?

Andreas
Blue Swirl - April 16, 2012, 6:53 p.m.
On Mon, Apr 16, 2012 at 13:01, Andreas Färber <afaerber@suse.de> wrote:
> Am 15.04.2012 18:32, schrieb Blue Swirl:
>> Add simple m48t59 qtests, enable test only for Sparc32
>> and Sparc64. On PPC, the device is behind PCI bus.
>>
>> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
>> ---
>>  tests/Makefile      |    5 +
>>  tests/m48t59-test.c |  259 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 264 insertions(+)
>>  create mode 100644 tests/m48t59-test.c
>>
>> diff --git a/tests/Makefile b/tests/Makefile
>> index baf1d70..04a8050 100644
>> --- a/tests/Makefile
>> +++ b/tests/Makefile
>> @@ -20,6 +20,10 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
>>  # really in libqtest, not in the testcases themselves.
>>  check-qtest-i386-y = tests/rtc-test
>>  check-qtest-x86_64-y = $(check-qtest-i386-y)
>> +#check-qtest-ppc-y = tests/m48t59-test
>> +#check-qtest-ppc64-y = tests/m48t59-test
>> +check-qtest-sparc-y = tests/m48t59-test
>> +check-qtest-sparc64-y = tests/m48t59-test
>
> For Stefan not to hang you, you should probably add $(EXESUF). ;)
>
> Some way of not duplicating the test four times would be nice. Maybe
> $(check-qtest-m48t59-y) or at least $(check-qtest-sparc-y) for sparc64?

Maybe, but in addition to testing the internal operation of the
device, it's also interesting to see if the device is wired to the
board correctly. Sparc32, Sparc64 and PPC* have different ways to
access the M48T59 device. For PPC32 vs. PPC64 I agree. We could also
limit the full set of tests only to one architecture, others could
just probe for the device.

>
> Andreas
>
> --
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg

Patch

diff --git a/tests/Makefile b/tests/Makefile
index baf1d70..04a8050 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -20,6 +20,10 @@  check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 # really in libqtest, not in the testcases themselves.
 check-qtest-i386-y = tests/rtc-test
 check-qtest-x86_64-y = $(check-qtest-i386-y)
+#check-qtest-ppc-y = tests/m48t59-test
+#check-qtest-ppc64-y = tests/m48t59-test
+check-qtest-sparc-y = tests/m48t59-test
+check-qtest-sparc64-y = tests/m48t59-test

 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h
tests/test-qmp-commands.h

@@ -64,6 +68,7 @@  tests/test-qmp-input-strict$(EXESUF):
tests/test-qmp-input-strict.o $(test-qapi-
 tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o
tests/test-qmp-marshal.o $(test-qapi-obj-y)

 tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y)
+tests/m48t59-test$(EXESUF): tests/m48t59-test.o $(trace-obj-y)

 # QTest rules

diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c
new file mode 100644
index 0000000..5179681
--- /dev/null
+++ b/tests/m48t59-test.c
@@ -0,0 +1,259 @@ 
+/*
+ * QTest testcase for the M48T59 and M48T08 real-time clocks
+ *
+ * Based on MC146818 RTC test:
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "libqtest.h"
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define RTC_SECONDS             0x9
+#define RTC_MINUTES             0xa
+#define RTC_HOURS               0xb
+
+#define RTC_DAY_OF_WEEK         0xc
+#define RTC_DAY_OF_MONTH        0xd
+#define RTC_MONTH               0xe
+#define RTC_YEAR                0xf
+
+static uint32_t base;
+static uint16_t reg_base = 0x1ff0; /* 0x7f0 for m48t02 */
+static int base_year;
+static bool use_mmio;
+
+static uint8_t cmos_read_mmio(uint8_t reg)
+{
+    uint8_t data;
+
+    memread(base + (uint32_t)reg_base + (uint32_t)reg, &data, 1);
+    return data;
+}
+
+static void cmos_write_mmio(uint8_t reg, uint8_t val)
+{
+    uint8_t data = val;
+
+    memwrite(base + (uint32_t)reg_base + (uint32_t)reg, &data, 1);
+}
+
+static uint8_t cmos_read_ioio(uint8_t reg)
+{
+    outw(base + 0, reg_base + (uint16_t)reg);
+    return inb(base + 3);
+}
+
+static void cmos_write_ioio(uint8_t reg, uint8_t val)
+{
+    outw(base + 0, reg_base + (uint16_t)reg);
+    outb(base + 3, val);
+}
+
+static uint8_t cmos_read(uint8_t reg)
+{
+    if (use_mmio) {
+        return cmos_read_mmio(reg);
+    } else {
+        return cmos_read_ioio(reg);
+    }
+}
+
+static void cmos_write(uint8_t reg, uint8_t val)
+{
+    if (use_mmio) {
+        cmos_write_mmio(reg, val);
+    } else {
+        cmos_write_ioio(reg, val);
+    }
+}
+
+static int bcd2dec(int value)
+{
+    return (((value >> 4) & 0x0F) * 10) + (value & 0x0F);
+}
+
+static int tm_cmp(struct tm *lhs, struct tm *rhs)
+{
+    time_t a, b;
+    struct tm d1, d2;
+
+    memcpy(&d1, lhs, sizeof(d1));
+    memcpy(&d2, rhs, sizeof(d2));
+
+    a = mktime(&d1);
+    b = mktime(&d2);
+
+    if (a < b) {
+        return -1;
+    } else if (a > b) {
+        return 1;
+    }
+
+    return 0;
+}
+
+#if 0
+static void print_tm(struct tm *tm)
+{
+    printf("%04d-%02d-%02d %02d:%02d:%02d %+02ld\n",
+           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+           tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff);
+}
+#endif
+
+static void cmos_get_date_time(struct tm *date)
+{
+    int sec, min, hour, mday, mon, year;
+    time_t ts;
+    struct tm dummy;
+
+    sec = cmos_read(RTC_SECONDS);
+    min = cmos_read(RTC_MINUTES);
+    hour = cmos_read(RTC_HOURS);
+    mday = cmos_read(RTC_DAY_OF_MONTH);
+    mon = cmos_read(RTC_MONTH);
+    year = cmos_read(RTC_YEAR);
+
+    sec = bcd2dec(sec);
+    min = bcd2dec(min);
+    hour = bcd2dec(hour);
+    mday = bcd2dec(mday);
+    mon = bcd2dec(mon);
+    year = bcd2dec(year);
+
+    ts = time(NULL);
+    localtime_r(&ts, &dummy);
+
+    date->tm_isdst = dummy.tm_isdst;
+    date->tm_sec = sec;
+    date->tm_min = min;
+    date->tm_hour = hour;
+    date->tm_mday = mday;
+    date->tm_mon = mon - 1;
+    date->tm_year = base_year + year - 1900;
+    date->tm_gmtoff = 0;
+
+    ts = mktime(date);
+}
+
+static void check_time(int wiggle)
+{
+    struct tm start, date[4], end;
+    struct tm *datep;
+    time_t ts;
+
+    /*
+     * This check assumes a few things.  First, we cannot guarantee that we get
+     * a consistent reading from the wall clock because we may hit an edge of
+     * the clock while reading.  To work around this, we read four
clock readings
+     * such that at least two of them should match.  We need to assume that one
+     * reading is corrupt so we need four readings to ensure that we have at
+     * least two consecutive identical readings
+     *
+     * It's also possible that we'll cross an edge reading the host clock so
+     * simply check to make sure that the clock reading is within the period of
+     * when we expect it to be.
+     */
+
+    ts = time(NULL);
+    gmtime_r(&ts, &start);
+
+    cmos_get_date_time(&date[0]);
+    cmos_get_date_time(&date[1]);
+    cmos_get_date_time(&date[2]);
+    cmos_get_date_time(&date[3]);
+
+    ts = time(NULL);
+    gmtime_r(&ts, &end);
+
+    if (tm_cmp(&date[0], &date[1]) == 0) {
+        datep = &date[0];
+    } else if (tm_cmp(&date[1], &date[2]) == 0) {
+        datep = &date[1];
+    } else if (tm_cmp(&date[2], &date[3]) == 0) {
+        datep = &date[2];
+    } else {
+        g_assert_not_reached();
+    }
+
+    if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
+        long t, s;
+
+        start.tm_isdst = datep->tm_isdst;
+
+        t = (long)mktime(datep);
+        s = (long)mktime(&start);
+        if (t < s) {
+            g_test_message("RTC is %ld second(s) behind
wall-clock\n", (s - t));
+        } else {
+            g_test_message("RTC is %ld second(s) ahead of
wall-clock\n", (t - s));
+        }
+
+        g_assert_cmpint(ABS(t - s), <=, wiggle);
+    }
+}
+
+static int wiggle = 2;
+
+static void bcd_check_time(void)
+{
+    if (strcmp(qtest_get_arch(), "sparc64") == 0) {
+        base = 0x74;
+        base_year = 1900;
+        use_mmio = false;
+    } else if (strcmp(qtest_get_arch(), "sparc") == 0) {
+        base = 0x71200000;
+        base_year = 1968;
+        use_mmio = true;
+    } else { /* PPC: need to map macio in PCI */
+        g_assert_not_reached();
+    }
+    check_time(wiggle);
+}
+
+/* success if no crash or abort */
+static void fuzz_registers(void)
+{
+    unsigned int i;
+
+    for (i = 0; i < 1000; i++) {
+        uint8_t reg, val;
+
+        reg = (uint8_t)g_test_rand_int_range(0, 16);
+        val = (uint8_t)g_test_rand_int_range(0, 256);
+
+        cmos_write(reg, val);
+        cmos_read(reg);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    QTestState *s = NULL;
+    int ret;
+
+    g_test_init(&argc, &argv, NULL);
+
+    s = qtest_start("-display none -rtc clock=vm");
+
+    qtest_add_func("/rtc/bcd/check-time", bcd_check_time);
+    qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
+    ret = g_test_run();
+
+    if (s) {
+        qtest_quit(s);
+    }