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 <mdr...@linux.vnet.ibm.com> --- qtest/qtest-rtc.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 170 insertions(+), 0 deletions(-) create mode 100644 qtest/qtest-rtc.c 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) -- 1.7.0.4