Adding such probing allows to clearly separate init vs runtime code, and to
place the probing logic into the init section for the CMOS case. Note both
the Xen shared_info page wallclock, and the EFI wallclock don't really have any
probing-specific logic. The shared_info wallclock will always be there if
booted as a Xen guest, while the EFI_GET_TIME method probing relies on checking
if it returns a value different than 0.
The panic message printed when Xen is unable to find a viable wallclock source
has been adjusted slightly, I believe the printed guidance still provides the
same amount of information to the user.
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Changes since v3:
- Remove ASSERT from cmos_read().
- Change indentation of panic message arguments in probe_wallclock().
- Add __init attribute.
Changes since v2:
- New in this version.
---
xen/arch/x86/time.c | 118 ++++++++++++++++++++++++++++++++++----------
1 file changed, 92 insertions(+), 26 deletions(-)
diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
index ec48805a2239..1dcbd9f520f5 100644
--- a/xen/arch/x86/time.c
+++ b/xen/arch/x86/time.c
@@ -1291,20 +1291,23 @@ static bool __get_cmos_time(struct rtc_time *rtc)
return t1 <= SECONDS(1) && t2 < MILLISECS(3);
}
-static bool __read_mostly opt_cmos_rtc_probe;
+static bool __initdata opt_cmos_rtc_probe;
boolean_param("cmos-rtc-probe", opt_cmos_rtc_probe);
-static bool cmos_rtc_probe(struct rtc_time *rtc_p)
+static bool __init cmos_rtc_probe(void)
{
unsigned int seconds = 60;
+ if ( !(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) )
+ return true;
+
+ if ( !opt_cmos_rtc_probe )
+ return false;
+
for ( ; ; )
{
- bool success = __get_cmos_time(rtc_p);
- struct rtc_time rtc = *rtc_p;
-
- if ( likely(!opt_cmos_rtc_probe) )
- return true;
+ struct rtc_time rtc;
+ bool success = __get_cmos_time(&rtc);
if ( !success ||
rtc.sec >= 60 || rtc.min >= 60 || rtc.hour >= 24 ||
@@ -1331,26 +1334,12 @@ static bool cmos_rtc_probe(struct rtc_time *rtc_p)
return false;
}
-static unsigned long get_cmos_time(void)
+
+static unsigned long cmos_rtc_read(void)
{
- unsigned long res;
struct rtc_time rtc;
- if ( efi_enabled(EFI_RS) )
- {
- res = efi_get_time();
- if ( res )
- return res;
- }
-
- if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
- opt_cmos_rtc_probe = false;
- else if ( system_state < SYS_STATE_smp_boot && !opt_cmos_rtc_probe )
- panic("System with no CMOS RTC advertised must be booted from EFI"
- " (or with command line option \"cmos-rtc-probe\")\n");
-
- if ( !cmos_rtc_probe(&rtc) )
- panic("No CMOS RTC found - system must be booted from EFI\n");
+ __get_cmos_time(&rtc);
return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
}
@@ -1533,12 +1522,85 @@ void rtc_guest_write(unsigned int port, unsigned int data)
}
}
-static unsigned long get_wallclock_time(void)
+static enum {
+ WALLCLOCK_UNSET,
+ WALLCLOCK_XEN,
+ WALLCLOCK_CMOS,
+ WALLCLOCK_EFI,
+} wallclock_source __ro_after_init;
+
+static const char *__init wallclock_type_to_string(void)
+{
+ switch ( wallclock_source )
+ {
+ case WALLCLOCK_XEN:
+ return "XEN";
+
+ case WALLCLOCK_CMOS:
+ return "CMOS RTC";
+
+ case WALLCLOCK_EFI:
+ return "EFI";
+
+ case WALLCLOCK_UNSET:
+ return "UNSET";
+ }
+
+ ASSERT_UNREACHABLE();
+ return "";
+}
+
+static void __init probe_wallclock(void)
{
+ ASSERT(wallclock_source == WALLCLOCK_UNSET);
+
if ( xen_guest )
+ {
+ wallclock_source = WALLCLOCK_XEN;
+ return;
+ }
+ if ( efi_enabled(EFI_RS) && efi_get_time() )
+ {
+ wallclock_source = WALLCLOCK_EFI;
+ return;
+ }
+ if ( cmos_rtc_probe() )
+ {
+ wallclock_source = WALLCLOCK_CMOS;
+ return;
+ }
+
+ panic("No usable wallclock found, probed:%s%s%s\n%s",
+ !opt_cmos_rtc_probe && !efi_enabled(EFI_RS) ? " None" : "",
+ opt_cmos_rtc_probe ? " CMOS" : "",
+ efi_enabled(EFI_RS) ? " EFI" : "",
+ !opt_cmos_rtc_probe
+ ? "Try with command line option \"cmos-rtc-probe\"\n"
+ : !efi_enabled(EFI_RS)
+ ? "System must be booted from EFI\n"
+ : "");
+}
+
+static unsigned long get_wallclock_time(void)
+{
+ switch ( wallclock_source )
+ {
+ case WALLCLOCK_XEN:
return read_xen_wallclock();
- return get_cmos_time();
+ case WALLCLOCK_CMOS:
+ return cmos_rtc_read();
+
+ case WALLCLOCK_EFI:
+ return efi_get_time();
+
+ case WALLCLOCK_UNSET:
+ /* Unexpected state - handled by the ASSERT_UNREACHABLE() below. */
+ break;
+ }
+
+ ASSERT_UNREACHABLE();
+ return 0;
}
/***************************************************************************
@@ -2463,6 +2525,10 @@ int __init init_xen_time(void)
open_softirq(TIME_CALIBRATE_SOFTIRQ, local_time_calibration);
+ probe_wallclock();
+
+ printk(XENLOG_INFO "Wallclock source: %s\n", wallclock_type_to_string());
+
/* NB. get_wallclock_time() can take over one second to execute. */
do_settime(get_wallclock_time(), 0, NOW());
--
2.46.0
On 09.09.2024 16:54, Roger Pau Monne wrote: > Adding such probing allows to clearly separate init vs runtime code, and to > place the probing logic into the init section for the CMOS case. Note both > the Xen shared_info page wallclock, and the EFI wallclock don't really have any > probing-specific logic. The shared_info wallclock will always be there if > booted as a Xen guest, while the EFI_GET_TIME method probing relies on checking > if it returns a value different than 0. > > The panic message printed when Xen is unable to find a viable wallclock source > has been adjusted slightly, I believe the printed guidance still provides the > same amount of information to the user. > > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
© 2016 - 2024 Red Hat, Inc.