Files
qemu-ios/contrib/ipod-emu/tests/test-rtc.c
T

156 lines
4.7 KiB
C

/* Régressions du D1759 sur le bus I²C réel de QEMU. */
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "sysemu/cpus.h"
#include "hw/arm/ipod_touch_pcf50633_pmu.c"
static QemuOptsList rtc_test_opts = {
.name = "rtc-test",
.head = QTAILQ_HEAD_INITIALIZER(rtc_test_opts.head),
.desc = {
{ .name = "base", .type = QEMU_OPT_STRING },
{ .name = "clock", .type = QEMU_OPT_STRING },
{ }
}
};
static I2CBus *bus;
static Pcf50633State *pmu;
static DeviceState *parent;
static void setup(const char *base, const char *clock)
{
QemuOpts *opts = qemu_opts_create(&rtc_test_opts, NULL, 0, &error_abort);
qemu_opt_set(opts, "base", base, &error_abort);
qemu_opt_set(opts, "clock", clock, &error_abort);
configure_rtc(opts);
qemu_opts_del(opts);
parent = qdev_new(TYPE_PCF50633);
bus = i2c_init_bus(parent, "rtc-test");
pmu = PCF50633(i2c_slave_create_simple(bus, TYPE_D1759, 0x73));
}
static void teardown(void)
{
qdev_unrealize(DEVICE(pmu));
object_unref(OBJECT(parent));
}
static void select_register(uint8_t reg, bool stop)
{
g_assert_cmpint(i2c_start_transfer(bus, 0x73, false), ==, 0);
g_assert_cmpint(i2c_send(bus, reg), ==, 0);
if (stop) {
i2c_end_transfer(bus);
}
g_assert_cmpint(i2c_start_transfer(bus, 0x73, true), ==, 0);
}
static uint32_t read_word(uint8_t reg, bool stop)
{
uint32_t value = 0;
select_register(reg, stop);
for (int i = 0; i < 4; i++) {
value |= (uint32_t)i2c_recv(bus) << (i * 8);
}
i2c_end_transfer(bus);
return value;
}
static void write_word(uint8_t reg, uint32_t value)
{
g_assert_cmpint(i2c_start_transfer(bus, 0x73, false), ==, 0);
g_assert_cmpint(i2c_send(bus, reg), ==, 0);
for (int i = 0; i < 4; i++) {
g_assert_cmpint(i2c_send(bus, value >> (i * 8)), ==, 0);
}
i2c_end_transfer(bus);
}
static void test_host_utc(void)
{
time_t before = time(NULL);
/* Le fuseau de l'hôte ne doit pas décaler le compteur Unix. */
setenv("TZ", "Pacific/Honolulu", 1);
tzset();
setup("utc", "host");
uint32_t value = read_word(0x5c, false);
g_assert_cmpuint(value, >=, before);
g_assert_cmpuint(value, <=, time(NULL));
g_assert_cmpuint(read_word(0x64, true), ==, 0);
teardown();
unsetenv("TZ");
tzset();
}
static void test_fixed_base(void)
{
setup("2024-02-29T23:59:59", "vm");
g_assert_cmpint(rtc_clock, ==, QEMU_CLOCK_VIRTUAL);
g_assert_cmpuint(read_word(0x5c, true), ==, 1709251199);
g_assert_cmpuint(read_word(0x5c, false), ==, 1709251199);
teardown();
}
static void test_snapshot_rollover(void)
{
setup("2024-02-29T23:59:59", "vm");
uint32_t boundaries[] = { 1709251199, 0x00ffffff, 0xffffffff };
for (size_t n = 0; n < G_N_ELEMENTS(boundaries); n++) {
pmu->rtc_tick_offset = boundaries[n];
select_register(0x5c, false);
uint32_t value = i2c_recv(bus);
/* Le temps change entre deux octets, après capture du premier. */
pmu->rtc_tick_offset++;
for (int i = 1; i < 4; i++) {
value |= (uint32_t)i2c_recv(bus) << (8 * i);
}
i2c_end_transfer(bus);
g_assert_cmpuint(value, ==, boundaries[n]);
g_assert_cmpuint(read_word(0x5c, true), ==, boundaries[n] + 1);
}
teardown();
}
static void test_offsets(void)
{
setup("2024-02-29T23:59:59", "vm");
uint32_t counter = read_word(0x5c, true);
uint32_t offsets[] = { 3600, (uint32_t)-86400, 0x12345678, 0 };
for (size_t i = 0; i < G_N_ELEMENTS(offsets); i++) {
write_word(0x64, offsets[i]);
g_assert_cmpuint(read_word(0x64, false), ==, offsets[i]);
g_assert_cmpuint(read_word(0x5c, true), ==, counter);
}
/* Un changement de date ne doit pas réécrire le compteur matériel. */
write_word(0x5c, 1234);
g_assert_cmpuint(read_word(0x5c, false), ==, counter);
teardown();
}
static void test_boot_registers(void)
{
setup("2024-02-29T23:59:59", "vm");
write_word(0x60, 0xffffffff);
g_assert_cmpuint(read_word(0x60, true), ==, 0);
g_assert_cmpuint(read_word(0x5c, false), ==, 1709251199);
g_assert_cmpuint(read_word(0x64, true), ==, 0);
teardown();
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
module_call_init(MODULE_INIT_QOM);
qemu_init_main_loop(&error_abort);
qemu_init_cpu_loop();
qemu_mutex_lock_iothread();
g_test_add_func("/ipod-rtc/host-utc", test_host_utc);
g_test_add_func("/ipod-rtc/fixed-base", test_fixed_base);
g_test_add_func("/ipod-rtc/snapshot-rollover", test_snapshot_rollover);
g_test_add_func("/ipod-rtc/offsets", test_offsets);
g_test_add_func("/ipod-rtc/boot-registers", test_boot_registers);
return g_test_run();
}