⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtc.c

📁 xen 3.2.2 源码
💻 C
字号:
/* * QEMU MC146818 RTC emulation *  * Copyright (c) 2003-2004 Fabrice Bellard *  * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */#include <asm/mc146818rtc.h>#include <asm/hvm/vpt.h>#include <asm/hvm/io.h>#include <asm/hvm/support.h>#include <asm/current.h>#define domain_vrtc(d)   (&(d)->arch.hvm_domain.pl_time.vrtc)#define vcpu_vrtc(vcpu)  (domain_vrtc((vcpu)->domain))#define vrtc_domain(rtc) (container_of((rtc), struct domain, \                                       arch.hvm_domain.pl_time.vrtc))#define vrtc_vcpu(rtc)   (vrtc_domain(rtc)->vcpu[0])static void rtc_periodic_cb(struct vcpu *v, void *opaque){    RTCState *s = opaque;    spin_lock(&s->lock);    s->hw.cmos_data[RTC_REG_C] |= 0xc0;    spin_unlock(&s->lock);}/* Enable/configure/disable the periodic timer based on the RTC_PIE and * RTC_RATE_SELECT settings */static void rtc_timer_update(RTCState *s){    int period_code, period;    struct vcpu *v = vrtc_vcpu(s);    ASSERT(spin_is_locked(&s->lock));    period_code = s->hw.cmos_data[RTC_REG_A] & RTC_RATE_SELECT;    if ( (period_code != 0) && (s->hw.cmos_data[RTC_REG_B] & RTC_PIE) )    {        if ( period_code <= 2 )            period_code += 7;        period = 1 << (period_code - 1); /* period in 32 Khz cycles */        period = DIV_ROUND((period * 1000000000ULL), 32768); /* period in ns */        create_periodic_time(v, &s->pt, period, RTC_IRQ,                             0, rtc_periodic_cb, s);    }    else    {        destroy_periodic_time(&s->pt);    }}static void rtc_set_time(RTCState *s);static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data){    RTCState *s = opaque;    spin_lock(&s->lock);    if ( (addr & 1) == 0 )    {        data &= 0x7f;        s->hw.cmos_index = data;        spin_unlock(&s->lock);        return (data < RTC_CMOS_SIZE);    }    if ( s->hw.cmos_index >= RTC_CMOS_SIZE )    {        spin_unlock(&s->lock);        return 0;    }    switch ( s->hw.cmos_index )    {    case RTC_SECONDS_ALARM:    case RTC_MINUTES_ALARM:    case RTC_HOURS_ALARM:        s->hw.cmos_data[s->hw.cmos_index] = data;        break;    case RTC_SECONDS:    case RTC_MINUTES:    case RTC_HOURS:    case RTC_DAY_OF_WEEK:    case RTC_DAY_OF_MONTH:    case RTC_MONTH:    case RTC_YEAR:        s->hw.cmos_data[s->hw.cmos_index] = data;        /* if in set mode, do not update the time */        if ( !(s->hw.cmos_data[RTC_REG_B] & RTC_SET) )            rtc_set_time(s);        break;    case RTC_REG_A:        /* UIP bit is read only */        s->hw.cmos_data[RTC_REG_A] = (data & ~RTC_UIP) |            (s->hw.cmos_data[RTC_REG_A] & RTC_UIP);        rtc_timer_update(s);        break;    case RTC_REG_B:        if ( data & RTC_SET )        {            /* set mode: reset UIP mode */            s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP;        }        else        {            /* if disabling set mode, update the time */            if ( s->hw.cmos_data[RTC_REG_B] & RTC_SET )                rtc_set_time(s);        }        s->hw.cmos_data[RTC_REG_B] = data;        rtc_timer_update(s);        break;    case RTC_REG_C:    case RTC_REG_D:        /* cannot write to them */        break;    }    spin_unlock(&s->lock);    return 1;}static inline int to_bcd(RTCState *s, int a){    if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )        return a;    else        return ((a / 10) << 4) | (a % 10);}static inline int from_bcd(RTCState *s, int a){    if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )        return a;    else        return ((a >> 4) * 10) + (a & 0x0f);}static void rtc_set_time(RTCState *s){    struct tm *tm = &s->current_tm;    unsigned long before, after; /* XXX s_time_t */          ASSERT(spin_is_locked(&s->lock));    before = mktime(tm->tm_year, tm->tm_mon, tm->tm_mday,		    tm->tm_hour, tm->tm_min, tm->tm_sec);        tm->tm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);    tm->tm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);    tm->tm_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS] & 0x7f);    if ( !(s->hw.cmos_data[RTC_REG_B] & 0x02) &&         (s->hw.cmos_data[RTC_HOURS] & 0x80) )        tm->tm_hour += 12;    tm->tm_wday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_WEEK]);    tm->tm_mday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_MONTH]);    tm->tm_mon = from_bcd(s, s->hw.cmos_data[RTC_MONTH]) - 1;    tm->tm_year = from_bcd(s, s->hw.cmos_data[RTC_YEAR]) + 100;    after = mktime(tm->tm_year, tm->tm_mon, tm->tm_mday,                   tm->tm_hour, tm->tm_min, tm->tm_sec);    send_timeoffset_req(after - before);}static void rtc_copy_date(RTCState *s){    const struct tm *tm = &s->current_tm;    struct domain *d = vrtc_domain(s);    ASSERT(spin_is_locked(&s->lock));    if ( s->time_offset_seconds != d->time_offset_seconds )    {        s->current_tm = gmtime(get_localtime(d));        s->time_offset_seconds = d->time_offset_seconds;    }    s->hw.cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);    s->hw.cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);    if ( s->hw.cmos_data[RTC_REG_B] & RTC_24H )    {        /* 24 hour format */        s->hw.cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);    }    else    {        /* 12 hour format */        s->hw.cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);        if ( tm->tm_hour >= 12 )            s->hw.cmos_data[RTC_HOURS] |= 0x80;    }    s->hw.cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);    s->hw.cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);    s->hw.cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);    s->hw.cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);}/* month is between 0 and 11. */static int get_days_in_month(int month, int year){    static const int days_tab[12] = {         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31     };    int d;    if ( (unsigned)month >= 12 )        return 31;    d = days_tab[month];    if ( month == 1 )        if ( (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) )            d++;    return d;}/* update 'tm' to the next second */static void rtc_next_second(RTCState *s){    struct tm *tm = &s->current_tm;    int days_in_month;    struct domain *d = vrtc_domain(s);    ASSERT(spin_is_locked(&s->lock));    if ( s->time_offset_seconds != d->time_offset_seconds )    {        s->current_tm = gmtime(get_localtime(d));        s->time_offset_seconds = d->time_offset_seconds;    }    tm->tm_sec++;    if ( (unsigned)tm->tm_sec >= 60 )    {        tm->tm_sec = 0;        tm->tm_min++;        if ( (unsigned)tm->tm_min >= 60 )        {            tm->tm_min = 0;            tm->tm_hour++;            if ( (unsigned)tm->tm_hour >= 24 )            {                tm->tm_hour = 0;                /* next day */                tm->tm_wday++;                if ( (unsigned)tm->tm_wday >= 7 )                    tm->tm_wday = 0;                days_in_month = get_days_in_month(tm->tm_mon,                                                   tm->tm_year + 1900);                tm->tm_mday++;                if ( tm->tm_mday < 1 )                {                    tm->tm_mday = 1;                }                else if ( tm->tm_mday > days_in_month )                {                    tm->tm_mday = 1;                    tm->tm_mon++;                    if ( tm->tm_mon >= 12 )                    {                        tm->tm_mon = 0;                        tm->tm_year++;                    }                }            }        }    }}static void rtc_update_second(void *opaque){    RTCState *s = opaque;    spin_lock(&s->lock);    /* if the oscillator is not in normal operation, we do not update */    if ( (s->hw.cmos_data[RTC_REG_A] & RTC_DIV_CTL) != RTC_REF_CLCK_32KHZ )    {        s->next_second_time += 1000000000ULL;        set_timer(&s->second_timer, s->next_second_time);    }    else    {        rtc_next_second(s);                if ( !(s->hw.cmos_data[RTC_REG_B] & RTC_SET) )            s->hw.cmos_data[RTC_REG_A] |= RTC_UIP;        /* Delay time before update cycle */        set_timer(&s->second_timer2, s->next_second_time + 244000);    }    spin_unlock(&s->lock);}static void rtc_update_second2(void *opaque){    RTCState *s = opaque;    struct domain *d = vrtc_domain(s);    spin_lock(&s->lock);    if ( !(s->hw.cmos_data[RTC_REG_B] & RTC_SET) )        rtc_copy_date(s);    /* check alarm */    if ( s->hw.cmos_data[RTC_REG_B] & RTC_AIE )    {        if ( ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||              from_bcd(s, s->hw.cmos_data[RTC_SECONDS_ALARM]) ==              s->current_tm.tm_sec) &&             ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||              from_bcd(s, s->hw.cmos_data[RTC_MINUTES_ALARM]) ==              s->current_tm.tm_min) &&             ((s->hw.cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||              from_bcd(s, s->hw.cmos_data[RTC_HOURS_ALARM]) ==              s->current_tm.tm_hour) )        {            s->hw.cmos_data[RTC_REG_C] |= 0xa0;             hvm_isa_irq_deassert(d, RTC_IRQ);            hvm_isa_irq_assert(d, RTC_IRQ);        }    }    /* update ended interrupt */    if ( (s->hw.cmos_data[RTC_REG_B] & (RTC_UIE|RTC_SET)) == RTC_UIE )    {        s->hw.cmos_data[RTC_REG_C] |= 0x90;         hvm_isa_irq_deassert(d, RTC_IRQ);        hvm_isa_irq_assert(d, RTC_IRQ);    }    /* clear update in progress bit */    s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP;    s->next_second_time += 1000000000ULL;    set_timer(&s->second_timer, s->next_second_time);    spin_unlock(&s->lock);}static uint32_t rtc_ioport_read(RTCState *s, uint32_t addr){    int ret;    if ( (addr & 1) == 0 )        return 0xff;    spin_lock(&s->lock);    switch ( s->hw.cmos_index )    {    case RTC_SECONDS:    case RTC_MINUTES:    case RTC_HOURS:    case RTC_DAY_OF_WEEK:    case RTC_DAY_OF_MONTH:    case RTC_MONTH:    case RTC_YEAR:        ret = s->hw.cmos_data[s->hw.cmos_index];        break;    case RTC_REG_A:        ret = s->hw.cmos_data[s->hw.cmos_index];        break;    case RTC_REG_C:        ret = s->hw.cmos_data[s->hw.cmos_index];        hvm_isa_irq_deassert(vrtc_domain(s), RTC_IRQ);        s->hw.cmos_data[RTC_REG_C] = 0x00;        break;    default:        ret = s->hw.cmos_data[s->hw.cmos_index];        break;    }    spin_unlock(&s->lock);    return ret;}static int handle_rtc_io(    int dir, uint32_t port, uint32_t bytes, uint32_t *val){    struct RTCState *vrtc = vcpu_vrtc(current);    if ( bytes != 1 )    {        gdprintk(XENLOG_WARNING, "HVM_RTC bas access\n");        return 1;    }        if ( dir == IOREQ_WRITE )    {        if ( rtc_ioport_write(vrtc, port, (uint8_t)*val) )            return 1;    }    else if ( vrtc->hw.cmos_index < RTC_CMOS_SIZE )    {        *val = rtc_ioport_read(vrtc, port);        return 1;    }    return 0;}void rtc_migrate_timers(struct vcpu *v){    RTCState *s = vcpu_vrtc(v);    if ( v->vcpu_id == 0 )    {        migrate_timer(&s->second_timer, v->processor);        migrate_timer(&s->second_timer2, v->processor);    }}/* Save RTC hardware state */static int rtc_save(struct domain *d, hvm_domain_context_t *h){    RTCState *s = domain_vrtc(d);    int rc;    spin_lock(&s->lock);    rc = hvm_save_entry(RTC, 0, h, &s->hw);    spin_unlock(&s->lock);    return rc;}/* Reload the hardware state from a saved domain */static int rtc_load(struct domain *d, hvm_domain_context_t *h){    RTCState *s = domain_vrtc(d);    spin_lock(&s->lock);    /* Restore the registers */    if ( hvm_load_entry(RTC, h, &s->hw) != 0 )    {        spin_unlock(&s->lock);        return -EINVAL;    }    /* Reset the wall-clock time.  In normal running, this runs with host      * time, so let's keep doing that. */    s->current_tm = gmtime(get_localtime(d));    rtc_copy_date(s);    s->next_second_time = NOW() + 1000000000ULL;    stop_timer(&s->second_timer);    set_timer(&s->second_timer2, s->next_second_time);    /* Reset the periodic interrupt timer based on the registers */    rtc_timer_update(s);    spin_unlock(&s->lock);    return 0;}HVM_REGISTER_SAVE_RESTORE(RTC, rtc_save, rtc_load, 1, HVMSR_PER_DOM);void rtc_init(struct vcpu *v, int base){    RTCState *s = vcpu_vrtc(v);    spin_lock_init(&s->lock);    s->pt.source = PTSRC_isa;    s->hw.cmos_data[RTC_REG_A] = RTC_REF_CLCK_32KHZ | 6; /* ~1kHz */    s->hw.cmos_data[RTC_REG_B] = RTC_24H;    s->hw.cmos_data[RTC_REG_C] = 0;    s->hw.cmos_data[RTC_REG_D] = RTC_VRT;    s->current_tm = gmtime(get_localtime(v->domain));    spin_lock(&s->lock);    rtc_copy_date(s);    spin_unlock(&s->lock);    init_timer(&s->second_timer, rtc_update_second, s, v->processor);    init_timer(&s->second_timer2, rtc_update_second2, s, v->processor);    s->next_second_time = NOW() + 1000000000ULL;    set_timer(&s->second_timer2, s->next_second_time);    register_portio_handler(v->domain, base, 2, handle_rtc_io);}void rtc_deinit(struct domain *d){    RTCState *s = domain_vrtc(d);    destroy_periodic_time(&s->pt);    kill_timer(&s->second_timer);    kill_timer(&s->second_timer2);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -