Xen-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
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.


      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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox