Patchwork [RFC,v1,3/4] qtest: add basic rtc test module

login
register
mail settings
Submitter Michael Roth
Date Feb. 4, 2011, 1:49 p.m.
Message ID <1296827392-1291-4-git-send-email-mdroth@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/81891/
State New
Headers show

Comments

Michael Roth - Feb. 4, 2011, 1:49 p.m.
This test module initializes a basic system with an ISA bus, a PIC
(currently not used in the test), and an RTC. Currently only drift
testing is performed at various frequencies. This is currently more
of a proof of concept than a rigorous test, and will be improved
over time.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qtest/qtest-rtc.c |  170 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 170 insertions(+), 0 deletions(-)
 create mode 100644 qtest/qtest-rtc.c

Patch

diff --git a/qtest/qtest-rtc.c b/qtest/qtest-rtc.c
new file mode 100644
index 0000000..a494fd5
--- /dev/null
+++ b/qtest/qtest-rtc.c
@@ -0,0 +1,170 @@ 
+#include <stdio.h>
+#include "qtest/qtest.h"
+#include "sysemu.h"
+#include "hw/pc.h"
+#include "hw/isa.h"
+#include "hw/irq.h"
+#include "hw/mc146818rtc.h"
+
+#define RAM_SIZE_B 128 * 1024 * 1024
+#define DEBUG_QT
+#ifdef DEBUG_QT
+#define TRACE(msg, ...) do { \
+    fprintf(stderr, "%s:%s():L%d: " msg "\n", \
+        __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
+} while(0)
+#else
+#define TRACE(msg, ...) \
+    do { } while (0)
+#endif
+
+static enum {
+    HANDLER_MODE_NORMAL = 0,
+    HANDLER_MODE_DRIFT_TEST_INIT,
+    HANDLER_MODE_DRIFT_TEST_STARTED,
+    HANDLER_MODE_ONESHOT_TEST_INIT,
+    HANDLER_MODE_ONESHOT_TEST_STARTED,
+} handler_mode = HANDLER_MODE_NORMAL;
+
+static uint32_t irq_count, irq_count_max;
+static struct timeval ts1, ts2;
+
+static void cmos_write(int addr, unsigned int val)
+{
+    cpu_outb(0x70, addr - 0x70);
+    cpu_outb(0x71, val);
+}
+
+static unsigned int cmos_read(int addr)
+{
+    cpu_outb(0x70, addr - 0x70);
+    return cpu_inb(0x71);
+}
+
+static void rtc_irq_handler(void *opaque, int n, int level)
+{
+    int rtc_reg_c = 0;
+    if (level) {
+        switch (handler_mode) {
+        case HANDLER_MODE_DRIFT_TEST_INIT:
+            irq_count = 0;
+            gettimeofday(&ts1, NULL);
+            handler_mode = HANDLER_MODE_DRIFT_TEST_STARTED;
+            break;
+        case HANDLER_MODE_DRIFT_TEST_STARTED:
+            if (++irq_count == irq_count_max) { 
+                gettimeofday(&ts2, NULL);
+                handler_mode = HANDLER_MODE_NORMAL;
+            }
+            break;
+        default:
+            break;
+        }
+        /* reset irq line */
+        rtc_reg_c = cmos_read(0x7C);
+    }
+    //TRACE("irq: %d, level: %d, 0x7c: %d", n, level, rtc_reg_c);
+}
+
+#define DRIFT_RATIO_MAX .05
+
+static void test_drift(void)
+{
+    uint32_t hz, start, stop, duration_ms, exp_duration_ms;
+    float drift_ratio;
+
+    handler_mode = HANDLER_MODE_NORMAL;
+    /* enable timer interrupts */
+    cmos_write(0x7B, cmos_read(0x7B) | 0x40);
+
+    /* set frequency to 2hz (32*1024khz >> (div - 1)), div is bottom 4 bits */
+    cmos_write(0x7A, (cmos_read(0x7A) & 0xF0) | 0x0F);
+    hz = 2;
+    irq_count_max = hz*5;
+    handler_mode = HANDLER_MODE_DRIFT_TEST_INIT;
+    while (handler_mode != HANDLER_MODE_NORMAL) {
+        sleep(1);
+    }
+    start = ts1.tv_sec * 1000 * 1000 + ts1.tv_usec;
+    stop = ts2.tv_sec * 1000 * 1000 + ts2.tv_usec;
+    duration_ms = (stop - start) / 1000;
+    exp_duration_ms = irq_count_max / 2 * 1000;
+    drift_ratio = fabs(1.0 - (float)duration_ms/exp_duration_ms);
+    TRACE("hz: %u, duration_ms: %u, exp_duration: %u, drift ratio: %f",
+           hz, duration_ms, exp_duration_ms, drift_ratio);
+    assert(drift_ratio <= .05);
+
+    /* set frequency to 1024hz */
+    cmos_write(0x7A, (cmos_read(0x7A) & 0xF0) | 0x06);
+    hz = 1024;
+    irq_count_max = hz * 5;
+    handler_mode = HANDLER_MODE_DRIFT_TEST_INIT;
+    while (handler_mode != HANDLER_MODE_NORMAL) {
+        sleep(1);
+    }
+    start = ts1.tv_sec * 1000 * 1000 + ts1.tv_usec;
+    stop = ts2.tv_sec * 1000 * 1000 + ts2.tv_usec;
+    duration_ms = (stop - start) / 1000;
+    exp_duration_ms = irq_count_max / hz * 1000;
+    drift_ratio = fabs(1.0 - (float)duration_ms/exp_duration_ms);
+    TRACE("hz: %u, duration_ms: %u, exp_duration: %u, drift ratio: %f",
+           hz, duration_ms, exp_duration_ms, drift_ratio);
+    assert(drift_ratio <= .05);
+}
+
+static void qtest_rtc_init(void)
+{
+    ram_addr_t below_4g_mem_size, above_4g_mem_size;
+    IsaIrqState *isa_irq_state;
+    ISADevice *rtc_state;
+    qemu_irq *rtc_irq, *cpu_irq, *isa_irq, *i8259;
+
+    /* allocate ram */
+    pc_memory_init(RAM_SIZE_B, NULL, NULL, NULL,
+                   &below_4g_mem_size, &above_4g_mem_size);
+    /*
+    pc_cmos_init(below_4g_mem_size, above_4g_mem_size, NULL,
+                 NULL, NULL, NULL, NULL);
+                 */
+
+    /* set up the isa bus. we'll use an intercept handler for the rtc
+     * interrupts, but set the PIC up in case we want to use it later
+     */
+    cpu_irq = pc_allocate_cpu_irq();
+    i8259 = i8259_init(cpu_irq[0]);
+    isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state));
+    isa_irq_state->i8259 = i8259;
+    isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
+    isa_bus_new(NULL);
+    isa_bus_irqs(isa_irq);
+
+    /* set up rtc */
+    rtc_irq = qemu_allocate_irqs(rtc_irq_handler, NULL, 1);
+    rtc_state = rtc_init(2000, rtc_irq[0]);
+}
+
+static void qtest_rtc_cleanup(void)
+{
+}
+
+static void *qtest_rtc_start(void *thread_args)
+{
+    test_drift();
+    //g_assert(false);
+    qemu_system_shutdown_request();
+    return NULL;
+}
+
+static QTestModule qtest_module = {
+    .name = "rtc",
+    .init = qtest_rtc_init,
+    .cleanup = qtest_rtc_cleanup,
+    .start = qtest_rtc_start,
+};
+
+static void qtest_module_register(void)
+{
+    qtest_register(&qtest_module);
+}
+
+qtest_init_registry(qtest_module_register)