* [PATCH v2 0/4] x86/time: CMOS RTC century byte
@ 2026-07-02 9:25 Jan Beulich
2026-07-02 9:29 ` [PATCH v2 1/4] x86/time: CMOS RTC may run in binary mode Jan Beulich
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Jan Beulich @ 2026-07-02 9:25 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Roger Pau Monné, Teddy Astie
While meanwhile we at least consume this ourselves, I'm still surprised that we
got away with also not emulating it for HVM guests.
There's now some other (more or less related) cleanup here as well.
1: x86/time: CMOS RTC may run in binary mode
2: time: shorten year determination loop
3: x86/vRTC: the use_timer field is a boolean one
4: x86/vRTC: support century field
Jan
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/4] x86/time: CMOS RTC may run in binary mode
2026-07-02 9:25 [PATCH v2 0/4] x86/time: CMOS RTC century byte Jan Beulich
@ 2026-07-02 9:29 ` 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
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Jan Beulich @ 2026-07-02 9:29 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Roger Pau Monné, Teddy Astie
Indicating it would always use BCD mode is just wrong (and then the
comment there said the opposite). All halfway recent (and really all 64-
bit capable) systems having a CMOS RTC should properly indicate the mode
in control register B.
Make use of the flag, but provide a fallback mechanism in case people run
into systems not matching the above assumption. Additionally, when binary
mode is indicated and when "cmos-rtc-probe" is in use (but "cmos-rtc-bcd"
isn't), probe whether the clock really runs in binary mode. (This probing,
sadly, can take up to 10 seconds.)
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v2: New.
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -339,6 +339,14 @@ parameter to "stable:socket".
Specify the event count threshold for raising Corrected Machine Check
Interrupts. Specifying zero disables CMCI handling.
+### cmos-rtc-bcd (x86)
+> `= <boolean>`
+
+> Default: `false`
+
+Flag to indicate the CMOS Real Time Clock uses BCD mode irrespective of
+control register B indicating binary mode.
+
### cmos-rtc-probe (x86)
> `= <boolean>`
--- a/xen/arch/x86/include/asm/mc146818rtc.h
+++ b/xen/arch/x86/include/asm/mc146818rtc.h
@@ -96,7 +96,6 @@ bool is_cmos_port(unsigned int port, uns
#ifndef RTC_PORT
#define RTC_PORT(x) (0x70 + (x))
-#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
#endif
/*
--- a/xen/arch/x86/time.c
+++ b/xen/arch/x86/time.c
@@ -1250,6 +1250,9 @@ mktime (unsigned int year, unsigned int
)*60 + sec; /* finally seconds */
}
+static bool __ro_after_init opt_cmos_rtc_bcd;
+boolean_param("cmos-rtc-bcd", opt_cmos_rtc_bcd);
+
struct rtc_time {
unsigned int year, mon, day, hour, min, sec;
};
@@ -1285,7 +1288,7 @@ static bool __get_cmos_time(struct rtc_t
if ( acpi_gbl_FADT.century && acpi_gbl_FADT.century < 0x80 )
century = CMOS_READ(acpi_gbl_FADT.century);
- bcd = RTC_ALWAYS_BCD || !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY);
+ bcd = opt_cmos_rtc_bcd || !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY);
spin_unlock_irqrestore(&rtc_lock, flags);
@@ -1353,6 +1356,48 @@ static bool __init cmos_rtc_probe(void)
return false;
}
+static inline bool __init attr_const is_bcd(unsigned int x)
+{
+ return (x & 0xf) < 10 && (x >> 4) < 10;
+}
+
+static void __init cmos_rtc_probe_bcd(void)
+{
+ bool bcd;
+ unsigned long flags;
+
+ if ( opt_cmos_rtc_bcd )
+ return;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ bcd = !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ if ( bcd )
+ return;
+
+ for ( unsigned int seclo = 0; ; )
+ {
+ struct rtc_time rtc;
+
+ if ( !__get_cmos_time(&rtc) ||
+ !is_bcd(rtc.sec) ||
+ !is_bcd(rtc.min) ||
+ !is_bcd(rtc.hour) ||
+ !is_bcd(rtc.day) ||
+ !is_bcd(rtc.mon) )
+ return;
+
+ if ( seclo > (rtc.sec & 0xf) )
+ break;
+
+ seclo = rtc.sec & 0xf;
+ }
+
+ printk(XENLOG_WARNING "CMOS RTC indicates binary mode but uses BCD\n");
+
+ opt_cmos_rtc_bcd = true;
+}
static unsigned long cmos_rtc_read(void)
{
@@ -1614,6 +1659,10 @@ static void __init probe_wallclock(void)
if ( cmos_rtc_probe() )
{
wallclock_source = WALLCLOCK_CMOS;
+
+ if ( opt_cmos_rtc_probe )
+ cmos_rtc_probe_bcd();
+
return;
}
if ( efi_enabled(EFI_RS) && efi_get_time() )
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/4] time: shorten year determination loop
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-02 9:30 ` 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-02 9:31 ` [PATCH v2 4/4] x86/vRTC: support century field Jan Beulich
3 siblings, 1 reply; 7+ messages in thread
From: Jan Beulich @ 2026-07-02 9:30 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Julien Grall, Stefano Stabellini, Anthony PERARD,
Michal Orzel, Roger Pau Monné
For dates very far into the future (the MC146818 RTC's century byte can go
up to the 99th century), the present year-wise loop would become somewhat
inefficient (taking perhaps several thousand iterations). Prefix that loop
with a 400-year granular calculation (somewhat like the earlier loop does
for dates in the past).
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v2: New.
--- a/xen/common/time.c
+++ b/xen/common/time.c
@@ -27,6 +27,8 @@
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+#define DAYS_IN_400_YEARS (365 * 303 + 366 * 97)
+
/* How many days are in each month. */
static const unsigned short int __mon_lengths[2][12] = {
/* Normal years. */
@@ -57,7 +59,7 @@ struct tm gmtime(unsigned long t)
while ( t & (1UL<<39) )
{
y -= 400;
- t += ((unsigned long)(365 * 303 + 366 * 97)) * SECS_PER_DAY;
+ t += (unsigned long)DAYS_IN_400_YEARS * SECS_PER_DAY;
}
t &= (1UL << 40) - 1;
#endif
@@ -71,6 +73,11 @@ struct tm gmtime(unsigned long t)
tbuf.tm_sec = rem % 60;
/* January 1, 1970 was a Thursday. */
tbuf.tm_wday = (4 + days) % 7;
+ if ( days >= DAYS_IN_400_YEARS )
+ {
+ y += (days / DAYS_IN_400_YEARS) * 400;
+ days %= DAYS_IN_400_YEARS;
+ }
while ( days >= (rem = __isleap(y) ? 366 : 365) )
{
++y;
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 3/4] x86/vRTC: the use_timer field is a boolean one
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-02 9:30 ` [PATCH v2 2/4] time: shorten year determination loop Jan Beulich
@ 2026-07-02 9:30 ` Jan Beulich
2026-07-02 9:31 ` [PATCH v2 4/4] x86/vRTC: support century field Jan Beulich
3 siblings, 0 replies; 7+ messages in thread
From: Jan Beulich @ 2026-07-02 9:30 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Roger Pau Monné, Teddy Astie
... and hence wants to be of bool type.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v2: New.
--- a/xen/arch/x86/hvm/rtc.c
+++ b/xen/arch/x86/hvm/rtc.c
@@ -186,7 +186,7 @@ static void check_update_timer(RTCState
if (!(s->hw.cmos_data[RTC_REG_C] & RTC_UF) &&
!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
{
- s->use_timer = 1;
+ s->use_timer = true;
guest_usec = get_localtime_us(d) % USEC_PER_SEC;
if (guest_usec >= (USEC_PER_SEC - 244))
{
@@ -214,7 +214,7 @@ static void check_update_timer(RTCState
}
}
else
- s->use_timer = 0;
+ s->use_timer = false;
}
static void cf_check rtc_update_timer(void *opaque)
@@ -673,7 +673,7 @@ static uint32_t rtc_ioport_read(RTCState
break;
case RTC_REG_A:
ret = s->hw.cmos_data[s->hw.cmos_index];
- if ((s->use_timer == 0) && update_in_progress(s))
+ if ( !s->use_timer && update_in_progress(s) )
ret |= RTC_UIP;
break;
case RTC_REG_C:
--- a/xen/arch/x86/include/asm/hvm/vpt.h
+++ b/xen/arch/x86/include/asm/hvm/vpt.h
@@ -106,7 +106,9 @@ typedef struct RTCState {
s_time_t check_ticks_since;
int period;
uint8_t pt_dead_ticks;
- uint32_t use_timer;
+
+ bool use_timer;
+
spinlock_t lock;
} RTCState;
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 4/4] x86/vRTC: support century field
2026-07-02 9:25 [PATCH v2 0/4] x86/time: CMOS RTC century byte Jan Beulich
` (2 preceding siblings ...)
2026-07-02 9:30 ` [PATCH v2 3/4] x86/vRTC: the use_timer field is a boolean one Jan Beulich
@ 2026-07-02 9:31 ` Jan Beulich
3 siblings, 0 replies; 7+ messages in thread
From: Jan Beulich @ 2026-07-02 9:31 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Andrew Cooper, Roger Pau Monné, Teddy Astie
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>
---
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?
Now that we extend struct hvm_hw_rtc, should we perhaps save not only the
century, but also its index?
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? And
isn't it excessive overhead to go through rtc_set_time() when the field
is updated while SET is clear?
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;
+
+ 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;
+ }
+
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)
{
@@ -873,6 +940,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();
--- 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;
--- 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, uns
#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
**********************************************************************/
--- 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);
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/4] x86/time: CMOS RTC may run in binary mode
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é
0 siblings, 0 replies; 7+ messages in thread
From: Roger Pau Monné @ 2026-07-28 14:25 UTC (permalink / raw)
To: Jan Beulich; +Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Teddy Astie
On Thu, Jul 02, 2026 at 11:29:11AM +0200, Jan Beulich wrote:
> Indicating it would always use BCD mode is just wrong (and then the
> comment there said the opposite). All halfway recent (and really all 64-
> bit capable) systems having a CMOS RTC should properly indicate the mode
> in control register B.
>
> Make use of the flag, but provide a fallback mechanism in case people run
> into systems not matching the above assumption. Additionally, when binary
> mode is indicated and when "cmos-rtc-probe" is in use (but "cmos-rtc-bcd"
> isn't), probe whether the clock really runs in binary mode. (This probing,
> sadly, can take up to 10 seconds.)
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
> ---
> v2: New.
>
> --- a/docs/misc/xen-command-line.pandoc
> +++ b/docs/misc/xen-command-line.pandoc
> @@ -339,6 +339,14 @@ parameter to "stable:socket".
> Specify the event count threshold for raising Corrected Machine Check
> Interrupts. Specifying zero disables CMCI handling.
>
> +### cmos-rtc-bcd (x86)
> +> `= <boolean>`
> +
> +> Default: `false`
> +
> +Flag to indicate the CMOS Real Time Clock uses BCD mode irrespective of
> +control register B indicating binary mode.
> +
Likely too late for it now, but I get the feeling we should have
introduced a cmos option, with rtc-bcd and rtc-probe as boolean sub
options:
cmos = [ rtc-probe, rtc-bcd ]
> ### cmos-rtc-probe (x86)
> > `= <boolean>`
>
> --- a/xen/arch/x86/include/asm/mc146818rtc.h
> +++ b/xen/arch/x86/include/asm/mc146818rtc.h
> @@ -96,7 +96,6 @@ bool is_cmos_port(unsigned int port, uns
>
> #ifndef RTC_PORT
> #define RTC_PORT(x) (0x70 + (x))
> -#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
> #endif
>
> /*
> --- a/xen/arch/x86/time.c
> +++ b/xen/arch/x86/time.c
> @@ -1250,6 +1250,9 @@ mktime (unsigned int year, unsigned int
> )*60 + sec; /* finally seconds */
> }
>
> +static bool __ro_after_init opt_cmos_rtc_bcd;
> +boolean_param("cmos-rtc-bcd", opt_cmos_rtc_bcd);
> +
> struct rtc_time {
> unsigned int year, mon, day, hour, min, sec;
> };
> @@ -1285,7 +1288,7 @@ static bool __get_cmos_time(struct rtc_t
> if ( acpi_gbl_FADT.century && acpi_gbl_FADT.century < 0x80 )
> century = CMOS_READ(acpi_gbl_FADT.century);
>
> - bcd = RTC_ALWAYS_BCD || !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY);
> + bcd = opt_cmos_rtc_bcd || !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY);
>
> spin_unlock_irqrestore(&rtc_lock, flags);
>
> @@ -1353,6 +1356,48 @@ static bool __init cmos_rtc_probe(void)
> return false;
> }
>
> +static inline bool __init attr_const is_bcd(unsigned int x)
> +{
> + return (x & 0xf) < 10 && (x >> 4) < 10;
> +}
> +
> +static void __init cmos_rtc_probe_bcd(void)
> +{
> + bool bcd;
> + unsigned long flags;
> +
> + if ( opt_cmos_rtc_bcd )
> + return;
> +
> + spin_lock_irqsave(&rtc_lock, flags);
> + bcd = !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY);
> + spin_unlock_irqrestore(&rtc_lock, flags);
> +
> + if ( bcd )
> + return;
> +
> + for ( unsigned int seclo = 0; ; )
> + {
> + struct rtc_time rtc;
> +
> + if ( !__get_cmos_time(&rtc) ||
> + !is_bcd(rtc.sec) ||
> + !is_bcd(rtc.min) ||
> + !is_bcd(rtc.hour) ||
> + !is_bcd(rtc.day) ||
> + !is_bcd(rtc.mon) )
> + return;
> +
> + if ( seclo > (rtc.sec & 0xf) )
> + break;
> +
> + seclo = rtc.sec & 0xf;
Is there a risk of this loop triggering the watchdog, and hence we
should process softirqs in the loop? (or otherwise have some kind of
hard loop stop after certain iterations / time)
Oh, I now see the mention in the commit message and also note this is
done ahead of SMP and also ahead of the watchdog being enabled, hence
it can't trigger the watchdog.
Thanks, Roger.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/4] time: shorten year determination loop
2026-07-02 9:30 ` [PATCH v2 2/4] time: shorten year determination loop Jan Beulich
@ 2026-07-28 15:02 ` Roger Pau Monné
0 siblings, 0 replies; 7+ messages in thread
From: Roger Pau Monné @ 2026-07-28 15:02 UTC (permalink / raw)
To: Jan Beulich
Cc: xen-devel@lists.xenproject.org, Andrew Cooper, Julien Grall,
Stefano Stabellini, Anthony PERARD, Michal Orzel
On Thu, Jul 02, 2026 at 11:30:11AM +0200, Jan Beulich wrote:
> For dates very far into the future (the MC146818 RTC's century byte can go
> up to the 99th century), the present year-wise loop would become somewhat
> inefficient (taking perhaps several thousand iterations). Prefix that loop
> with a 400-year granular calculation (somewhat like the earlier loop does
> for dates in the past).
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger@xenproject.org>
Thanks, Roger.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-28 15:03 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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-02 9:31 ` [PATCH v2 4/4] x86/vRTC: support century field Jan Beulich
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.