虚拟机启动参数
 -rtc base=localtime,driftfix=slew

(gdb) bt
#0  0x00005555558f3184 in mc146818_rtc_init (bus=0x555557057dd0, base_year=2000, intercept_irq=0x0) at /home/work/qemu/hw/rtc/mc146818rtc.c:981
#1  0x0000555555945707 in pc_basic_device_init (pcms=0x555556a828a0, isa_bus=0x555557057dd0, gsi=0x555556cb66a0, rtc_state=0x7fffffffcf30, create_fdctrl=true, hpet_irqs=4) at /home/work/qemu/hw/i386/pc.c:1209
#2  0x0000555555949420 in pc_init1 (machine=0x555556a828a0, host_type=0x555555f7b5be "i440FX-pcihost", pci_type=0x555555f7b5b7 "i440FX") at /home/work/qemu/hw/i386/pc_piix.c:241
#3  0x0000555555949b38 in pc_init_v5_1 (machine=0x555556a828a0)    at /home/work/qemu/hw/i386/pc_piix.c:438
#4  0x0000555555ab289c in machine_run_board_init (machine=0x555556a828a0) at hw/core/machine.c:1134
#5  0x000055555599a506 in qemu_init (argc=91, argv=0x7fffffffd3d8, envp=0x7fffffffd6b8)    at /home/work/qemu/softmmu/vl.c:4356
#6  0x0000555555de7941 in main (argc=91, argv=0x7fffffffd3d8, envp=0x7fffffffd6b8) at /home/work/qemu/softmmu/main.c:48
 --> isa_realize_and_unref

softmmu/vl.c
 --> configure_rtc(qemu_find_opts_singleton("rtc"))

static void configure_rtc(QemuOpts *opts)
{
    const char *value;

    /* Set defaults */
    rtc_clock = QEMU_CLOCK_HOST;
    rtc_ref_start_datetime = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
    rtc_realtime_clock_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;

    ......
    
    value = qemu_opt_get(opts, "driftfix");
    if (value) {
        if (!strcmp(value, "slew")) {
            object_register_sugar_prop("mc146818rtc",     //模拟mc146818时钟芯片
                                       "lost_tick_policy",
                                       "slew");
        } else if (!strcmp(value, "none")) {
            /* discard is default */
        } else {
            error_report("invalid option value '%s'", value);
            exit(1);
        }
    }
}

模拟mc146818时钟芯片
hw/rtc/mc146818rtc.c
 --> type_init(mc146818rtc_register_types)
  --> c->realize = rtc_realizefn;

static void rtc_realizefn(DeviceState *dev, Error **errp)
{
    ISADevice *isadev = ISA_DEVICE(dev);
    RTCState *s = MC146818_RTC(dev);

    s->cmos_data[RTC_REG_A] = 0x26;
    s->cmos_data[RTC_REG_B] = 0x02;
    s->cmos_data[RTC_REG_C] = 0x00;
    s->cmos_data[RTC_REG_D] = 0x80;

    /* This is for historical reasons.  The default base year qdev property
     * was set to 2000 for most machine types before the century byte was
     * implemented.
     *
     * This if statement means that the century byte will be always 0
     * (at least until 2079...) for base_year = 1980, but will be set
     * correctly for base_year = 2000.
     */
    if (s->base_year == 2000) {
        s->base_year = 0;
    }

    rtc_set_date_from_host(isadev);   //获取服务器主机的时间

    switch (s->lost_tick_policy) {
#ifdef TARGET_I386
    case LOST_TICK_POLICY_SLEW:
        s->coalesced_timer =
            timer_new_ns(rtc_clock, rtc_coalesced_timer, s);
        break;
#endif
    case LOST_TICK_POLICY_DISCARD:
        break;
    default:
        error_setg(errp, "Invalid lost tick policy.");
        return;
    }

    s->periodic_timer = timer_new_ns(rtc_clock, rtc_periodic_timer, s);
    s->update_timer = timer_new_ns(rtc_clock, rtc_update_timer, s);
    check_update_timer(s);

    s->suspend_notifier.notify = rtc_notify_suspend;
    qemu_register_suspend_notifier(&s->suspend_notifier);

    memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2);
    isa_register_ioport(isadev, &s->io, RTC_ISA_BASE);

    /* register rtc 0x70 port for coalesced_pio */
    memory_region_set_flush_coalesced(&s->io);
    memory_region_init_io(&s->coalesced_io, OBJECT(s), &cmos_ops,
                          s, "rtc-index", 1);
    memory_region_add_subregion(&s->io, 0, &s->coalesced_io);
    memory_region_add_coalescing(&s->coalesced_io, 0, 1);

    qdev_set_legacy_instance_id(dev, RTC_ISA_BASE, 3);
    qemu_register_reset(rtc_reset, s);

    object_property_add_tm(OBJECT(s), "date", rtc_get_date);

    qdev_init_gpio_out(dev, &s->irq, 1);
    QLIST_INSERT_HEAD(&rtc_devices, s, link);
}


---------------------------------------------------------------------------------------
/**
 * QEMUClockType:
 *
 * The following clock types are available:
 *  
 * @QEMU_CLOCK_REALTIME: Real time clock
 *
 * The real time clock should be used only for stuff which does not
 * change the virtual machine state, as it runs even if the virtual
 * machine is stopped.
 *  
 * @QEMU_CLOCK_VIRTUAL: virtual clock
 *
 * The virtual clock only runs during the emulation. It stops
 * when the virtual machine is stopped.
 *
 * @QEMU_CLOCK_HOST: host clock
 *
 * The host clock should be used for device models that emulate accurate
 * real time sources. It will continue to run when the virtual machine
 * is suspended, and it will reflect system time changes the host may
 * undergo (e.g. due to NTP). 
 *
 * @QEMU_CLOCK_VIRTUAL_RT: realtime clock used for icount warp
 *  
 * Outside icount mode, this clock is the same as @QEMU_CLOCK_VIRTUAL.
 * In icount mode, this clock counts nanoseconds while the virtual
 * machine is running.  It is used to increase @QEMU_CLOCK_VIRTUAL
 * while the CPUs are sleeping and thus not executing instructions.
 */

typedef enum {
    QEMU_CLOCK_REALTIME = 0,
    QEMU_CLOCK_VIRTUAL = 1,
    QEMU_CLOCK_HOST = 2,
    QEMU_CLOCK_VIRTUAL_RT = 3,
    QEMU_CLOCK_MAX
} QEMUClockType;

typedef struct RTCState {
    ISADevice parent_obj;
    
    MemoryRegion io;
    MemoryRegion coalesced_io;
    uint8_t cmos_data[128];
    uint8_t cmos_index;
    int32_t base_year;
    uint64_t base_rtc;
    uint64_t last_update;
    int64_t offset; 
    qemu_irq irq;
    int it_shift;
    /* periodic timer */
    QEMUTimer *periodic_timer;
    int64_t next_periodic_time;
    /* update-ended timer */
    QEMUTimer *update_timer;
    uint64_t next_alarm_time;
    uint16_t irq_reinject_on_ack_count;
    uint32_t irq_coalesced;
    uint32_t period;
    QEMUTimer *coalesced_timer;
    Notifier clock_reset_notifier;
    LostTickPolicy lost_tick_policy;
    Notifier suspend_notifier;
    QLIST_ENTRY(RTCState) link;
} RTCState;

int64_t qemu_clock_get_ns(QEMUClockType type)
{
    switch (type) {
    case QEMU_CLOCK_REALTIME:
        return get_clock();
    default:
    case QEMU_CLOCK_VIRTUAL:
        if (use_icount) {
            return cpu_get_icount();
        } else {
            return cpu_get_clock();
        }
    case QEMU_CLOCK_HOST:
        return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime());
    case QEMU_CLOCK_VIRTUAL_RT:
        return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock());
    }
}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐