From: "Roger Pau Monné" <roger.pau@citrix.com>
To: Jan Beulich <jbeulich@suse.com>
Cc: "xen-devel@lists.xenproject.org" <xen-devel@lists.xenproject.org>,
Andrew Cooper <andrew.cooper3@citrix.com>,
Teddy Astie <teddy.astie@vates.tech>
Subject: Re: [PATCH v2 4/4] x86/vRTC: support century field
Date: Wed, 29 Jul 2026 10:45:05 +0200 [thread overview]
Message-ID: <amm9kWntPk1ZVUJi@macbook.local> (raw)
In-Reply-To: <eee7754d-a7ef-477c-a74d-2104291103bb@suse.com>
On Thu, Jul 02, 2026 at 11:31:14AM +0200, Jan Beulich wrote:
> Both ROMBIOS and SeaBIOS (with CONFIG_QEMU=y, as we build it) blindly
> assume availability of this field (at its conventional index 0x32); OVMF
> at least has code to inspect FADT. Hence we ought to have supported it
> virtually forever.
>
> As the index is beyond RTC_CMOS_SIZE, leverage the padding field in
> struct hvm_hw_rtc to hold its value. Update the field only when involved
> values are valid BCD century specifiers. Otherwise (for VMs migrated in
> from an older hypervisor) leave handling to the DM.
>
> This makes the Linux rtc-cmos driver report y3k compatibility.
>
> In the new rtc_check(), besides checking the new fields also check the
> pre-existing pad0 field.
>
> While extending xen-hvmctx.c:dump_rtc() also add RTC offset there.
>
> Fixes: 4ca161214355 ("[HVM] Move RTC emulation into the hypervisor")
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger@xenproject.org>
> ---
> Am I overly paranoid with the checking of the field, considering that
> Xen 3.x post-dates year 2000 and hence all firmware nowadays usable guests
> have ever run with should have been aware of the field? Or am I, quite the
> opposite, still not strict enough?
I think the checking is likely fine.
> Now that we extend struct hvm_hw_rtc, should we perhaps save not only the
> century, but also its index?
Hm, possibly for correctness, albeit I think this is unlikely to cause
issues. Likely better done in a separate patch?
>
> Likely more sanity checking could be added to rtc_check(), but that's for
> a separate patch imo.
>
> Isn't day-of-week handling flawed? If the field is brought out of sync
> with the other values, shouldn't it stay respectively out-of-sync?
I don't know that much about the RTC TBH.
> And
> isn't it excessive overhead to go through rtc_set_time() when the field
> is updated while SET is clear?
I think this is done because we don't call rtc_set_time() when RTC_SET
is activated in RTC_REG_B? We would need to change the logic a bit.
Is there a reason to propagate the changes to the DM even when SET is
not active?
> Perhaps we ought to also support alarm day/month features?
> ---
> v2: Don't re-purpose pad0 field of struct hvm_hw_rtc.
>
> --- 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 = {
> --- 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)
> --- a/xen/arch/x86/hvm/rtc.c
> +++ b/xen/arch/x86/hvm/rtc.c
> @@ -482,16 +482,27 @@ static int rtc_ioport_write(void *opaque
> 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:
> @@ -507,6 +518,7 @@ static int rtc_ioport_write(void *opaque
> 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;
> @@ -515,7 +527,10 @@ static int rtc_ioport_write(void *opaque
> /* 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);
> @@ -591,7 +606,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);
> @@ -629,6 +653,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)
> @@ -663,13 +693,17 @@ static uint32_t rtc_ioport_read(RTCState
> 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];
> @@ -718,7 +752,8 @@ static int cf_check handle_rtc_io(
> *val = 0xff;
> return X86EMUL_OKAY;
> }
> - else if ( vrtc->hw.cmos_index < RTC_CMOS_SIZE )
> + else if ( vrtc->hw.cmos_index < RTC_CMOS_SIZE ||
> + (vrtc->has_century && vrtc->hw.cmos_index == RTC_CENTURY) )
> {
> *val = rtc_ioport_read(vrtc);
> return X86EMUL_OKAY;
> @@ -760,6 +795,32 @@ static int cf_check rtc_save(struct vcpu
> 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;
It would be nice to also set ->has_century here, but the s struct is
a temporary stack allocation.
> +
> + return 0;
> +}
> +
> /* Reload the hardware state from a saved domain */
> static int cf_check rtc_load(struct domain *d, hvm_domain_context_t *h)
> {
> @@ -793,12 +854,18 @@ static int cf_check rtc_load(struct doma
> check_update_timer(s);
> alarm_timer_update(s);
>
> + if ( !s->hw.century )
> + {
> + s->has_century = false;
> + s->hw.century = 0;
Isn't this last line pointless? The if condition is !s->hw.century,
and hence s->hw.century must be 0 here?
Thanks, Roger.
prev parent reply other threads:[~2026-07-29 8:45 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-02 9:25 [PATCH v2 0/4] x86/time: CMOS RTC century byte Jan Beulich
2026-07-02 9:29 ` [PATCH v2 1/4] x86/time: CMOS RTC may run in binary mode Jan Beulich
2026-07-28 14:25 ` Roger Pau Monné
2026-07-02 9:30 ` [PATCH v2 2/4] time: shorten year determination loop Jan Beulich
2026-07-28 15:02 ` Roger Pau Monné
2026-07-02 9:30 ` [PATCH v2 3/4] x86/vRTC: the use_timer field is a boolean one Jan Beulich
2026-07-29 7:47 ` Roger Pau Monné
2026-07-02 9:31 ` [PATCH v2 4/4] x86/vRTC: support century field Jan Beulich
2026-07-29 8:45 ` Roger Pau Monné [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=amm9kWntPk1ZVUJi@macbook.local \
--to=roger.pau@citrix.com \
--cc=andrew.cooper3@citrix.com \
--cc=jbeulich@suse.com \
--cc=teddy.astie@vates.tech \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.