diff --git a/tools/libacpi/static_tables.c b/tools/libacpi/static_tables.c index 715f46fee0..11cbeced64 100644 --- a/tools/libacpi/static_tables.c +++ b/tools/libacpi/static_tables.c @@ -33,6 +33,8 @@ struct acpi_20_facs Facs = { #define ACPI_PM_TMR_BLK_BIT_WIDTH 0x20 #define ACPI_PM_TMR_BLK_BIT_OFFSET 0x00 +#define CMOS_CENTURY 0x32 /* Conventional index used also without ACPI */ + struct acpi_fadt Fadt = { .header = { .signature = ACPI_FADT_SIGNATURE, @@ -88,7 +90,9 @@ struct acpi_fadt Fadt = { .register_bit_width = ACPI_PM_TMR_BLK_BIT_WIDTH, .register_bit_offset = ACPI_PM_TMR_BLK_BIT_OFFSET, .address = ACPI_PM_TMR_BLK_ADDRESS_V1, - } + }, + + .century = CMOS_CENTURY, }; struct acpi_20_rsdt Rsdt = { diff --git a/tools/misc/xen-hvmctx.c b/tools/misc/xen-hvmctx.c index ecb7f054be..69b22fe7a4 100644 --- a/tools/misc/xen-hvmctx.c +++ b/tools/misc/xen-hvmctx.c @@ -311,7 +311,7 @@ static void dump_rtc(void) printf(" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x, index 0x%02x\n", r.cmos_data[8], r.cmos_data[9], r.cmos_data[10], r.cmos_data[11], r.cmos_data[12], r.cmos_data[13], r.cmos_index); - + printf(" century 0x%02x offset %"PRId64"\n", r.century, r.rtc_offset); } static void dump_hpet(void) diff --git a/xen/arch/x86/hvm/rtc.c b/xen/arch/x86/hvm/rtc.c index 78543ab87c..4e0d28d1ea 100644 --- a/xen/arch/x86/hvm/rtc.c +++ b/xen/arch/x86/hvm/rtc.c @@ -484,16 +484,27 @@ static int rtc_ioport_write(RTCState *s, uint32_t addr, uint32_t data) data &= 0x7f; s->hw.cmos_index = data; spin_unlock(&s->lock); - return (data < RTC_CMOS_SIZE); + return data < RTC_CMOS_SIZE || (s->has_century && data == RTC_CENTURY); } - if ( s->hw.cmos_index >= RTC_CMOS_SIZE ) + switch ( s->hw.cmos_index ) { + case 0 ... RTC_CMOS_SIZE - 1: + orig = s->hw.cmos_data[s->hw.cmos_index]; + break; + + case RTC_CENTURY: + if ( s->has_century ) + { + orig = s->hw.century; + break; + } + fallthrough; + default: spin_unlock(&s->lock); return 0; } - orig = s->hw.cmos_data[s->hw.cmos_index]; switch ( s->hw.cmos_index ) { case RTC_SECONDS_ALARM: @@ -509,6 +520,7 @@ static int rtc_ioport_write(RTCState *s, uint32_t addr, uint32_t data) case RTC_DAY_OF_MONTH: case RTC_MONTH: case RTC_YEAR: + case RTC_CENTURY: /* if in set mode, just write the register */ if ( (s->hw.cmos_data[RTC_REG_B] & RTC_SET) ) s->hw.cmos_data[s->hw.cmos_index] = data; @@ -517,7 +529,10 @@ static int rtc_ioport_write(RTCState *s, uint32_t addr, uint32_t data) /* Fetch the current time and update just this field. */ s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); - s->hw.cmos_data[s->hw.cmos_index] = data; + if ( s->hw.cmos_index != RTC_CENTURY ) + s->hw.cmos_data[s->hw.cmos_index] = data; + else + s->hw.century = data; rtc_set_time(s); } alarm_timer_update(s); @@ -593,7 +608,16 @@ static void rtc_set_time(RTCState *s) 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; + tm->tm_year = from_bcd(s, s->hw.cmos_data[RTC_YEAR]); + if ( s->has_century ) + { + unsigned int century = s->hw.century; + + BCD_TO_BIN(century); + tm->tm_year += century * 100 - epoch_year; + } + else + tm->tm_year += 100; after = mktime(get_year(tm->tm_year), tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); @@ -631,6 +655,12 @@ static void rtc_copy_date(RTCState *s) 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); + + if ( s->has_century ) + { + s->hw.century = get_year(tm->tm_year) / 100; + BIN_TO_BCD(s->hw.century); + } } static int update_in_progress(RTCState *s) @@ -658,7 +688,8 @@ static bool rtc_ioport_read(RTCState *s, uint32_t *val) spin_lock(&s->lock); - if ( s->hw.cmos_index >= RTC_CMOS_SIZE ) + if ( s->hw.cmos_index >= RTC_CMOS_SIZE && + (!s->has_century || s->hw.cmos_index != RTC_CENTURY) ) { spin_unlock(&s->lock); return false; @@ -673,13 +704,17 @@ static bool rtc_ioport_read(RTCState *s, uint32_t *val) case RTC_DAY_OF_MONTH: case RTC_MONTH: case RTC_YEAR: + case RTC_CENTURY: /* if not in set mode, adjust cmos before reading*/ if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); } - ret = s->hw.cmos_data[s->hw.cmos_index]; + if ( s->hw.cmos_index != RTC_CENTURY ) + ret = s->hw.cmos_data[s->hw.cmos_index]; + else + ret = s->hw.century; break; case RTC_REG_A: ret = s->hw.cmos_data[s->hw.cmos_index]; @@ -769,6 +804,32 @@ static int cf_check rtc_save(struct vcpu *v, hvm_domain_context_t *h) return rc; } +static int cf_check rtc_check(const struct domain *d, hvm_domain_context_t *h) +{ + const struct hvm_save_descriptor *desc = + (const struct hvm_save_descriptor *)&h->data[h->cur]; + struct hvm_hw_rtc s; + + if ( !has_vrtc(d) ) + return -ENODEV; + + if ( hvm_load_entry_zeroextend(RTC, h, &s) != 0 ) + return -ENODATA; + + if ( s.pad0 ) + return -EINVAL; + + for ( unsigned int i = 0; i < ARRAY_SIZE(s.pad1); ++i ) + if ( s.pad1[i] ) + return -EINVAL; + + if ( desc->length >= endof_field(struct hvm_hw_rtc, century) && + ((s.century & 0xf) >= 10 || (s.century >> 4) >= 10) ) + return -EINVAL; + + return 0; +} + /* Reload the hardware state from a saved domain */ static int cf_check rtc_load(struct domain *d, hvm_domain_context_t *h) { @@ -802,12 +863,15 @@ static int cf_check rtc_load(struct domain *d, hvm_domain_context_t *h) check_update_timer(s); alarm_timer_update(s); + if ( !s->hw.century ) + s->has_century = false; + spin_unlock(&s->lock); return 0; } -HVM_REGISTER_SAVE_RESTORE(RTC, rtc_save, NULL, rtc_load, 1, HVMSR_PER_DOM); +HVM_REGISTER_SAVE_RESTORE(RTC, rtc_save, rtc_check, rtc_load, 1, HVMSR_PER_DOM); void rtc_reset(struct domain *d) { @@ -882,6 +946,12 @@ void rtc_init(struct domain *d) s->hw.cmos_data[RTC_REG_C] = 0; s->hw.cmos_data[RTC_REG_D] = RTC_VRT; + /* + * By default we make the century byte available, unless an incoming save + * record says otherwise. + */ + s->has_century = true; + s->current_tm = gmtime(get_localtime(d)); s->start_time = NOW(); diff --git a/xen/arch/x86/include/asm/hvm/vpt.h b/xen/arch/x86/include/asm/hvm/vpt.h index 2f6bd28a82..f6bcec8090 100644 --- a/xen/arch/x86/include/asm/hvm/vpt.h +++ b/xen/arch/x86/include/asm/hvm/vpt.h @@ -109,6 +109,8 @@ typedef struct RTCState { bool use_timer; + bool has_century; + spinlock_t lock; } RTCState; diff --git a/xen/arch/x86/include/asm/mc146818rtc.h b/xen/arch/x86/include/asm/mc146818rtc.h index 2c02caba4d..c77c5bc216 100644 --- a/xen/arch/x86/include/asm/mc146818rtc.h +++ b/xen/arch/x86/include/asm/mc146818rtc.h @@ -37,6 +37,9 @@ bool is_cmos_port(unsigned int port, unsigned int bytes, #define RTC_REG_C 12 #define RTC_REG_D 13 +/* Conventional index used without (and typically also with) ACPI. */ +#define RTC_CENTURY 0x32 + /********************************************************************** * register details **********************************************************************/ diff --git a/xen/include/public/arch-x86/hvm/save.h b/xen/include/public/arch-x86/hvm/save.h index 9c4bfc7ebd..44d7924777 100644 --- a/xen/include/public/arch-x86/hvm/save.h +++ b/xen/include/public/arch-x86/hvm/save.h @@ -488,6 +488,8 @@ struct hvm_hw_rtc { uint8_t pad0; /* RTC offset from host time */ int64_t rtc_offset; + uint8_t century; + uint8_t pad1[7]; }; DECLARE_HVM_SAVE_TYPE(RTC, 11, struct hvm_hw_rtc);