qemu-patch-raspberry4/tests/m48t59-test.c
Thomas Huth 9c29830c90 tests/m48t59: Fix and re-enable the test for sparc
The m48t59 test has been disabled in commit baeddded5f
("sparc: disable qtest in make check"), likely due to some timing issues
in the bcd_check_time tests which might fail if it gets interrupted for
too long. It should be OK to re-enable this test if we make sure that we
do not run it on timing-sensitive machines, thus it should be OK if we only
run it in the g_test_slow() mode.

Additionally, there are two other issues:

First, the test can not run so easily on sparc64 anymore, since commit
f3b18f35a2 ("sun4u: switch m48t59 NVRAM to MMIO access")
moved the m48t59 device to the ebus instead, and for this you first
have to set up the corresponding PCI device (which is currently not
possible from within the m48t59 test). So we can only re-enable this
test on sparc, but not the sparc64 target.

Second, the fuzzing test is executed before the bcd-check-time test
(due to the naming of the tests), without having the base address set
up properly, so the fuzzing test does not really check anything at all.
Fix it by setting up the base address from the main function already
and by moving the qtest_start() to the tests themselves, so that each
test starts with a clean environment (since after the fuzzing, the clock
is unusable for the bcd-check-time test).

Signed-off-by: Thomas Huth <thuth@redhat.com>
2018-02-14 11:43:42 +01:00

267 lines
6 KiB
C

/*
* 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 "qemu/osdep.h"
#include "libqtest.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)
{
return readb(base + (uint32_t)reg_base + (uint32_t)reg);
}
static void cmos_write_mmio(uint8_t reg, uint8_t val)
{
uint8_t data = val;
writeb(base + (uint32_t)reg_base + (uint32_t)reg, data);
}
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;
#ifndef __sun__
date->tm_gmtoff = 0;
#endif
ts = mktime(date);
}
static QTestState *m48t59_qtest_start(void)
{
return qtest_start("-rtc clock=vm");
}
static void bcd_check_time(void)
{
struct tm start, date[4], end;
struct tm *datep;
time_t ts;
const int wiggle = 2;
QTestState *s = m48t59_qtest_start();
/*
* 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);
}
qtest_quit(s);
}
/* success if no crash or abort */
static void fuzz_registers(void)
{
unsigned int i;
QTestState *s = m48t59_qtest_start();
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);
if (reg == 7) {
/* watchdog setup register, may trigger system reset, skip */
continue;
}
cmos_write(reg, val);
cmos_read(reg);
}
qtest_quit(s);
}
static void base_setup(void)
{
const char *arch = qtest_get_arch();
if (g_str_equal(arch, "sparc")) {
/* Note: For sparc64, we'd need to map-in the PCI bridge memory first */
base = 0x71200000;
base_year = 1968;
use_mmio = true;
} else {
g_assert_not_reached();
}
}
int main(int argc, char **argv)
{
int ret;
base_setup();
g_test_init(&argc, &argv, NULL);
if (g_test_slow()) {
/* Do not run this in timing-sensitive environments */
qtest_add_func("/rtc/bcd-check-time", bcd_check_time);
}
qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
ret = g_test_run();
return ret;
}