All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] x86/time: prefer CMOS over EFI_GET_TIME
@ 2024-08-30  9:52 Roger Pau Monne
  2024-08-30  9:52 ` [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper Roger Pau Monne
  2024-08-30  9:52 ` [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME Roger Pau Monne
  0 siblings, 2 replies; 12+ messages in thread
From: Roger Pau Monne @ 2024-08-30  9:52 UTC (permalink / raw)
  To: xen-devel
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Roger Pau Monne,
	Jan Beulich, Andrew Cooper

Hello,

Workaround a common issue with x86 EFI implementations having broken
EFI_GET_TIME methods, and only resort to EFI_GET_TIME if CMOS is not
available.

First patch splits some of the CMOS probe and fetch logic into a
separate helper, so that patch 2 that actually performs the ordering
change is cleaner.

Thanks, Roger.

Roger Pau Monne (2):
  x86/time: split CMOS time fetching into wrapper
  x86/time: prefer CMOS over EFI_GET_TIME

 xen/arch/x86/time.c | 88 +++++++++++++++++++++++++++------------------
 1 file changed, 54 insertions(+), 34 deletions(-)

-- 
2.46.0



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper
  2024-08-30  9:52 [PATCH v2 0/2] x86/time: prefer CMOS over EFI_GET_TIME Roger Pau Monne
@ 2024-08-30  9:52 ` Roger Pau Monne
  2024-08-30 15:47   ` Andrew Cooper
  2024-09-03  6:24   ` Jan Beulich
  2024-08-30  9:52 ` [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME Roger Pau Monne
  1 sibling, 2 replies; 12+ messages in thread
From: Roger Pau Monne @ 2024-08-30  9:52 UTC (permalink / raw)
  To: xen-devel
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Roger Pau Monne,
	Jan Beulich, Andrew Cooper

Split the logic that deals with probing and fetching the CMOS time into a
separate helper.  While moving the code also take the opportunity to reduce the
scope of some local variables.

No functional change intended.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Changes since v1:
 - New in this version.
---
 xen/arch/x86/time.c | 72 +++++++++++++++++++++++++--------------------
 1 file changed, 40 insertions(+), 32 deletions(-)

diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
index a97d78484105..272ca2468ea6 100644
--- a/xen/arch/x86/time.c
+++ b/xen/arch/x86/time.c
@@ -1245,29 +1245,14 @@ static void __get_cmos_time(struct rtc_time *rtc)
         rtc->year += 100;
 }
 
-static unsigned long get_cmos_time(void)
+/* Returns true when fetching time from CMOS is successful. */
+static bool read_cmos_time(struct rtc_time *rtc, bool cmos_rtc_probe)
 {
-    unsigned long res, flags;
-    struct rtc_time rtc;
     unsigned int seconds = 60;
-    static bool __read_mostly cmos_rtc_probe;
-    boolean_param("cmos-rtc-probe", cmos_rtc_probe);
-
-    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)) )
-        cmos_rtc_probe = false;
-    else if ( system_state < SYS_STATE_smp_boot && !cmos_rtc_probe )
-        panic("System with no CMOS RTC advertised must be booted from EFI"
-              " (or with command line option \"cmos-rtc-probe\")\n");
 
     for ( ; ; )
     {
+        unsigned long flags;
         s_time_t start, t1, t2;
 
         spin_lock_irqsave(&rtc_lock, flags);
@@ -1285,33 +1270,56 @@ static unsigned long get_cmos_time(void)
         } while ( (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) &&
                   t2 < MILLISECS(3) );
 
-        __get_cmos_time(&rtc);
+        __get_cmos_time(rtc);
 
         spin_unlock_irqrestore(&rtc_lock, flags);
 
-        if ( likely(!cmos_rtc_probe) ||
-             t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
-             rtc.sec >= 60 || rtc.min >= 60 || rtc.hour >= 24 ||
-             !rtc.day || rtc.day > 31 ||
-             !rtc.mon || rtc.mon > 12 )
-            break;
+        if ( likely(!cmos_rtc_probe) )
+            return true;
+
+        if ( t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
+             rtc->sec >= 60 || rtc->min >= 60 || rtc->hour >= 24 ||
+             !rtc->day || rtc->day > 31 ||
+             !rtc->mon || rtc->mon > 12 )
+            return false;
 
         if ( seconds < 60 )
         {
-            if ( rtc.sec != seconds )
-            {
-                cmos_rtc_probe = false;
+            if ( rtc->sec != seconds )
                 acpi_gbl_FADT.boot_flags &= ~ACPI_FADT_NO_CMOS_RTC;
-            }
-            break;
+            return true;
         }
 
         process_pending_softirqs();
 
-        seconds = rtc.sec;
+        seconds = rtc->sec;
     }
 
-    if ( unlikely(cmos_rtc_probe) )
+    ASSERT_UNREACHABLE();
+    return false;
+}
+
+static unsigned long get_cmos_time(void)
+{
+    struct rtc_time rtc;
+    static bool __read_mostly cmos_rtc_probe;
+    boolean_param("cmos-rtc-probe", cmos_rtc_probe);
+
+    if ( efi_enabled(EFI_RS) )
+    {
+        unsigned long res = efi_get_time();
+
+        if ( res )
+            return res;
+    }
+
+    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
+        cmos_rtc_probe = false;
+    else if ( system_state < SYS_STATE_smp_boot && !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 ( unlikely(!read_cmos_time(&rtc, cmos_rtc_probe)) )
         panic("No CMOS RTC found - system must be booted from EFI\n");
 
     return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
-- 
2.46.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME
  2024-08-30  9:52 [PATCH v2 0/2] x86/time: prefer CMOS over EFI_GET_TIME Roger Pau Monne
  2024-08-30  9:52 ` [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper Roger Pau Monne
@ 2024-08-30  9:52 ` Roger Pau Monne
  2024-08-30 16:31   ` Andrew Cooper
  1 sibling, 1 reply; 12+ messages in thread
From: Roger Pau Monne @ 2024-08-30  9:52 UTC (permalink / raw)
  To: xen-devel
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Roger Pau Monne,
	Jan Beulich, Andrew Cooper

The EFI_GET_TIME implementation is well known to be broken for many firmware
implementations, for Xen the result on such implementations are:

----[ Xen-4.19-unstable  x86_64  debug=y  Tainted:   C    ]----
CPU:    0
RIP:    e008:[<0000000062ccfa70>] 0000000062ccfa70
[...]
Xen call trace:
   [<0000000062ccfa70>] R 0000000062ccfa70
   [<00000000732e9a3f>] S 00000000732e9a3f
   [<ffff82d04034f34f>] F arch/x86/time.c#get_cmos_time+0x1b3/0x26e
   [<ffff82d04045926f>] F init_xen_time+0x28/0xa4
   [<ffff82d040454bc4>] F __start_xen+0x1ee7/0x2578
   [<ffff82d040203334>] F __high_start+0x94/0xa0

Pagetable walk from 0000000062ccfa70:
 L4[0x000] = 000000207ef1c063 ffffffffffffffff
 L3[0x001] = 000000005d6c0063 ffffffffffffffff
 L2[0x116] = 8000000062c001e3 ffffffffffffffff (PSE)

****************************************
Panic on CPU 0:
FATAL PAGE FAULT
[error_code=0011]
Faulting linear address: 0000000062ccfa70
****************************************

Swap the preference to default to CMOS first, and EFI later, in an attempt to
use EFI_GET_TIME as a last resort option only.  Note that Linux for example
doesn't allow calling the get_time method, and instead provides a dummy handler
that unconditionally returns EFI_UNSUPPORTED on x86-64.

Such change in the preferences requires some re-arranging of the function
logic, so that panic messages with workaround suggestions are suitably printed.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
 xen/arch/x86/time.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
index 272ca2468ea6..0eee954809a9 100644
--- a/xen/arch/x86/time.c
+++ b/xen/arch/x86/time.c
@@ -1305,24 +1305,36 @@ static unsigned long get_cmos_time(void)
     static bool __read_mostly cmos_rtc_probe;
     boolean_param("cmos-rtc-probe", cmos_rtc_probe);
 
+    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
+        cmos_rtc_probe = false;
+
+    if ( (!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) ||
+          cmos_rtc_probe) && read_cmos_time(&rtc, cmos_rtc_probe) )
+        return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
+
     if ( efi_enabled(EFI_RS) )
     {
         unsigned long res = efi_get_time();
 
         if ( res )
             return res;
+
+        panic("Broken EFI_GET_TIME %s\n",
+              !cmos_rtc_probe ? "try booting with \"cmos-rtc-probe\" option"
+                              : "and no CMOS RTC found");
     }
 
-    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
-        cmos_rtc_probe = false;
-    else if ( system_state < SYS_STATE_smp_boot && !cmos_rtc_probe )
+    /*
+     * No viable clock source found.  Attempt to provide some guidance to the
+     * user about possible workarounds to boot Xen on the system.
+     */
+    ASSERT(system_state < SYS_STATE_smp_boot);
+
+    if ( !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 ( unlikely(!read_cmos_time(&rtc, cmos_rtc_probe)) )
-        panic("No CMOS RTC found - system must be booted from EFI\n");
-
-    return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
+    panic("No CMOS RTC found - system must be booted from EFI\n");
 }
 
 static unsigned int __ro_after_init cmos_alias_mask;
-- 
2.46.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper
  2024-08-30  9:52 ` [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper Roger Pau Monne
@ 2024-08-30 15:47   ` Andrew Cooper
  2024-09-03  6:24   ` Jan Beulich
  1 sibling, 0 replies; 12+ messages in thread
From: Andrew Cooper @ 2024-08-30 15:47 UTC (permalink / raw)
  To: Roger Pau Monne, xen-devel
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Jan Beulich

On 30/08/2024 10:52 am, Roger Pau Monne wrote:
> Split the logic that deals with probing and fetching the CMOS time into a
> separate helper.  While moving the code also take the opportunity to reduce the
> scope of some local variables.
>
> No functional change intended.
>
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>

This does look like a straight rearrangement, so

Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>, although...

> +static unsigned long get_cmos_time(void)
> +{
> +    struct rtc_time rtc;
> +    static bool __read_mostly cmos_rtc_probe;
> +    boolean_param("cmos-rtc-probe", cmos_rtc_probe);
> +
> +    if ( efi_enabled(EFI_RS) )
> +    {
> +        unsigned long res = efi_get_time();
> +
> +        if ( res )
> +            return res;
> +    }
> +
> +    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
> +        cmos_rtc_probe = false;
> +    else if ( system_state < SYS_STATE_smp_boot && !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 ( unlikely(!read_cmos_time(&rtc, cmos_rtc_probe)) )
>          panic("No CMOS RTC found - system must be booted from EFI\n");

... this really does show some (preexisting) tangled logic.

All of this should live in an init function, and not be re-evaluated
each time we call get_wallclock_time(), not that we call it very often.

cmos_rtc_probe should be __initdata or at least __ro_after_init, but it
gets written on every pass through the function on most systems.

Lets focus on not crashing.  Cleanup can come later.

~Andrew


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME
  2024-08-30  9:52 ` [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME Roger Pau Monne
@ 2024-08-30 16:31   ` Andrew Cooper
  2024-09-02  8:13     ` Jan Beulich
  2024-09-02  8:45     ` Roger Pau Monné
  0 siblings, 2 replies; 12+ messages in thread
From: Andrew Cooper @ 2024-08-30 16:31 UTC (permalink / raw)
  To: Roger Pau Monne, xen-devel
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Jan Beulich

On 30/08/2024 10:52 am, Roger Pau Monne wrote:
> The EFI_GET_TIME implementation is well known to be broken for many firmware
> implementations, for Xen the result on such implementations are:
>
> ----[ Xen-4.19-unstable  x86_64  debug=y  Tainted:   C    ]----
> CPU:    0
> RIP:    e008:[<0000000062ccfa70>] 0000000062ccfa70
> [...]
> Xen call trace:
>    [<0000000062ccfa70>] R 0000000062ccfa70
>    [<00000000732e9a3f>] S 00000000732e9a3f
>    [<ffff82d04034f34f>] F arch/x86/time.c#get_cmos_time+0x1b3/0x26e
>    [<ffff82d04045926f>] F init_xen_time+0x28/0xa4
>    [<ffff82d040454bc4>] F __start_xen+0x1ee7/0x2578
>    [<ffff82d040203334>] F __high_start+0x94/0xa0
>
> Pagetable walk from 0000000062ccfa70:
>  L4[0x000] = 000000207ef1c063 ffffffffffffffff
>  L3[0x001] = 000000005d6c0063 ffffffffffffffff
>  L2[0x116] = 8000000062c001e3 ffffffffffffffff (PSE)
>
> ****************************************
> Panic on CPU 0:
> FATAL PAGE FAULT
> [error_code=0011]
> Faulting linear address: 0000000062ccfa70
> ****************************************
>
> Swap the preference to default to CMOS first, and EFI later, in an attempt to
> use EFI_GET_TIME as a last resort option only.  Note that Linux for example
> doesn't allow calling the get_time method, and instead provides a dummy handler
> that unconditionally returns EFI_UNSUPPORTED on x86-64.
>
> Such change in the preferences requires some re-arranging of the function
> logic, so that panic messages with workaround suggestions are suitably printed.
>
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> ---
>  xen/arch/x86/time.c | 26 +++++++++++++++++++-------
>  1 file changed, 19 insertions(+), 7 deletions(-)
>
> diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
> index 272ca2468ea6..0eee954809a9 100644
> --- a/xen/arch/x86/time.c
> +++ b/xen/arch/x86/time.c
> @@ -1305,24 +1305,36 @@ static unsigned long get_cmos_time(void)
>      static bool __read_mostly cmos_rtc_probe;
>      boolean_param("cmos-rtc-probe", cmos_rtc_probe);
>  
> +    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
> +        cmos_rtc_probe = false;
> +
> +    if ( (!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) ||
> +          cmos_rtc_probe) && read_cmos_time(&rtc, cmos_rtc_probe) )
> +        return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
> +
>      if ( efi_enabled(EFI_RS) )
>      {
>          unsigned long res = efi_get_time();
>  
>          if ( res )
>              return res;
> +
> +        panic("Broken EFI_GET_TIME %s\n",
> +              !cmos_rtc_probe ? "try booting with \"cmos-rtc-probe\" option"
> +                              : "and no CMOS RTC found");
>      }
>  
> -    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
> -        cmos_rtc_probe = false;
> -    else if ( system_state < SYS_STATE_smp_boot && !cmos_rtc_probe )
> +    /*
> +     * No viable clock source found.  Attempt to provide some guidance to the
> +     * user about possible workarounds to boot Xen on the system.
> +     */
> +    ASSERT(system_state < SYS_STATE_smp_boot);
> +
> +    if ( !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 ( unlikely(!read_cmos_time(&rtc, cmos_rtc_probe)) )
> -        panic("No CMOS RTC found - system must be booted from EFI\n");
> -
> -    return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
> +    panic("No CMOS RTC found - system must be booted from EFI\n");
>  }
>  
>  static unsigned int __ro_after_init cmos_alias_mask;

Hmm, I know I said "fix the crash first, cleanup later" on the prior
patch, but I'm tempted to retract that.  This is very hard to follow.

Going back to first principles, at runtime we need USE_{XEN,RTC,EFI} to
select between the various methods available.

At boot, we need to figure out what to do.  I think we want an
init_wallclock_time() split out of get_cmos_time() (which is badly named
anyway, given EFI), and called from init_xen_time() only.  In
particular, the init stuff should not be re-evaluated in
time_{suspend,resume}().

Various details we have to work with:

 * ACPI_FADT_NO_CMOS_RTC, although we know this is falsely set on some
platforms, hence the whole probing logic.

 * Booted on an EFI system, although we know EFI_GET_TIME is broken on
millions of systems, and we only find this out with a #PF in the middle
of EFI-RS.  Furthermore, more-major-OSes-than-us strictly veto the use
of this service, and it's not only Linux; it's Windows too.

Personally, I think "cmos-rtc-probe" is inappropriate/insufficient.  It
ought to be wc-time=guess|xen|rtc|efi.  We should be able to influence
the behaviour more than a boolean "probe or not".  The Xen case might be
better as "hypervisor", although I can't see any evidence of Viridian
having a virtualised wallclock interface.

I think the init logic wants to be:
 * If ACPI says we have an RTC, use it.
 * If ACPI says we have no RTC, probe to see if it's really there.
 * If we genuinely seem to not have an RTC, probe EFI.  This would be
quite invasive in the #PF handler, but not impossible.


That said, I'm still holding out hope that we can simply delete Xen's
need for wallclock time.  Xen only uses wallclock time for the
non-default console_timestamps=date, but even then it's uncorrelate with
dom0 changing the platform time, leading to actively-misleading log
files.  There's a reason why "time since boot" is the norm.

~Andrew


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME
  2024-08-30 16:31   ` Andrew Cooper
@ 2024-09-02  8:13     ` Jan Beulich
  2024-09-02  8:45     ` Roger Pau Monné
  1 sibling, 0 replies; 12+ messages in thread
From: Jan Beulich @ 2024-09-02  8:13 UTC (permalink / raw)
  To: Andrew Cooper
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Roger Pau Monne,
	xen-devel

On 30.08.2024 18:31, Andrew Cooper wrote:
> I think the init logic wants to be:
>  * If ACPI says we have an RTC, use it.
>  * If ACPI says we have no RTC, probe to see if it's really there.
>  * If we genuinely seem to not have an RTC, probe EFI.  This would be
> quite invasive in the #PF handler, but not impossible.

And would not necessarily be reliable: We can't exclude that paths taken
are (slightly) different for multiple invocations.

Jan



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME
  2024-08-30 16:31   ` Andrew Cooper
  2024-09-02  8:13     ` Jan Beulich
@ 2024-09-02  8:45     ` Roger Pau Monné
  2024-09-02  8:56       ` Jan Beulich
  1 sibling, 1 reply; 12+ messages in thread
From: Roger Pau Monné @ 2024-09-02  8:45 UTC (permalink / raw)
  To: Andrew Cooper
  Cc: xen-devel, Daniel P. Smith, Marek Marczykowski-Górecki,
	Jan Beulich

On Fri, Aug 30, 2024 at 05:31:04PM +0100, Andrew Cooper wrote:
> On 30/08/2024 10:52 am, Roger Pau Monne wrote:
> > The EFI_GET_TIME implementation is well known to be broken for many firmware
> > implementations, for Xen the result on such implementations are:
> >
> > ----[ Xen-4.19-unstable  x86_64  debug=y  Tainted:   C    ]----
> > CPU:    0
> > RIP:    e008:[<0000000062ccfa70>] 0000000062ccfa70
> > [...]
> > Xen call trace:
> >    [<0000000062ccfa70>] R 0000000062ccfa70
> >    [<00000000732e9a3f>] S 00000000732e9a3f
> >    [<ffff82d04034f34f>] F arch/x86/time.c#get_cmos_time+0x1b3/0x26e
> >    [<ffff82d04045926f>] F init_xen_time+0x28/0xa4
> >    [<ffff82d040454bc4>] F __start_xen+0x1ee7/0x2578
> >    [<ffff82d040203334>] F __high_start+0x94/0xa0
> >
> > Pagetable walk from 0000000062ccfa70:
> >  L4[0x000] = 000000207ef1c063 ffffffffffffffff
> >  L3[0x001] = 000000005d6c0063 ffffffffffffffff
> >  L2[0x116] = 8000000062c001e3 ffffffffffffffff (PSE)
> >
> > ****************************************
> > Panic on CPU 0:
> > FATAL PAGE FAULT
> > [error_code=0011]
> > Faulting linear address: 0000000062ccfa70
> > ****************************************
> >
> > Swap the preference to default to CMOS first, and EFI later, in an attempt to
> > use EFI_GET_TIME as a last resort option only.  Note that Linux for example
> > doesn't allow calling the get_time method, and instead provides a dummy handler
> > that unconditionally returns EFI_UNSUPPORTED on x86-64.
> >
> > Such change in the preferences requires some re-arranging of the function
> > logic, so that panic messages with workaround suggestions are suitably printed.
> >
> > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> > ---
> >  xen/arch/x86/time.c | 26 +++++++++++++++++++-------
> >  1 file changed, 19 insertions(+), 7 deletions(-)
> >
> > diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
> > index 272ca2468ea6..0eee954809a9 100644
> > --- a/xen/arch/x86/time.c
> > +++ b/xen/arch/x86/time.c
> > @@ -1305,24 +1305,36 @@ static unsigned long get_cmos_time(void)
> >      static bool __read_mostly cmos_rtc_probe;
> >      boolean_param("cmos-rtc-probe", cmos_rtc_probe);
> >  
> > +    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
> > +        cmos_rtc_probe = false;
> > +
> > +    if ( (!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) ||
> > +          cmos_rtc_probe) && read_cmos_time(&rtc, cmos_rtc_probe) )
> > +        return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
> > +
> >      if ( efi_enabled(EFI_RS) )
> >      {
> >          unsigned long res = efi_get_time();
> >  
> >          if ( res )
> >              return res;
> > +
> > +        panic("Broken EFI_GET_TIME %s\n",
> > +              !cmos_rtc_probe ? "try booting with \"cmos-rtc-probe\" option"
> > +                              : "and no CMOS RTC found");
> >      }
> >  
> > -    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
> > -        cmos_rtc_probe = false;
> > -    else if ( system_state < SYS_STATE_smp_boot && !cmos_rtc_probe )
> > +    /*
> > +     * No viable clock source found.  Attempt to provide some guidance to the
> > +     * user about possible workarounds to boot Xen on the system.
> > +     */
> > +    ASSERT(system_state < SYS_STATE_smp_boot);
> > +
> > +    if ( !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 ( unlikely(!read_cmos_time(&rtc, cmos_rtc_probe)) )
> > -        panic("No CMOS RTC found - system must be booted from EFI\n");
> > -
> > -    return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);
> > +    panic("No CMOS RTC found - system must be booted from EFI\n");
> >  }
> >  
> >  static unsigned int __ro_after_init cmos_alias_mask;
> 
> Hmm, I know I said "fix the crash first, cleanup later" on the prior
> patch, but I'm tempted to retract that.  This is very hard to follow.

Yeah, I was attempting to refrain myself from fully re-writing the logic.

> Going back to first principles, at runtime we need USE_{XEN,RTC,EFI} to
> select between the various methods available.
> 
> At boot, we need to figure out what to do.  I think we want an
> init_wallclock_time() split out of get_cmos_time() (which is badly named
> anyway, given EFI), and called from init_xen_time() only.  In
> particular, the init stuff should not be re-evaluated in
> time_{suspend,resume}().

I also disliked the current arrangement, and adding a probe function
crossed my mind, I simply wanted to avoid this fix growing to a 10
patch series, but I think it's inevitable.

> Various details we have to work with:
> 
>  * ACPI_FADT_NO_CMOS_RTC, although we know this is falsely set on some
> platforms, hence the whole probing logic.
> 
>  * Booted on an EFI system, although we know EFI_GET_TIME is broken on
> millions of systems, and we only find this out with a #PF in the middle
> of EFI-RS.  Furthermore, more-major-OSes-than-us strictly veto the use
> of this service, and it's not only Linux; it's Windows too.
> 
> Personally, I think "cmos-rtc-probe" is inappropriate/insufficient.  It
> ought to be wc-time=guess|xen|rtc|efi.  We should be able to influence
> the behaviour more than a boolean "probe or not".  The Xen case might be
> better as "hypervisor", although I can't see any evidence of Viridian
> having a virtualised wallclock interface.
> 
> I think the init logic wants to be:
>  * If ACPI says we have an RTC, use it.
>  * If ACPI says we have no RTC, probe to see if it's really there.
>  * If we genuinely seem to not have an RTC, probe EFI.  This would be
> quite invasive in the #PF handler, but not impossible.

My plan would be to use EFI as a last resort if available, and hence
not probe it.  It would however be nice to give the user a better
error message than a #PF in the hypervisor with some random stack
trace.

Is #PF the only fault that we can expect from EFI_GET_TIME?  That's
what I've seen on some of the systems, but I wouldn't for example
discard #GP or #UD as also possible outcomes?

> 
> 
> That said, I'm still holding out hope that we can simply delete Xen's
> need for wallclock time.  Xen only uses wallclock time for the
> non-default console_timestamps=date, but even then it's uncorrelate with
> dom0 changing the platform time, leading to actively-misleading log
> files.  There's a reason why "time since boot" is the norm.

But there's a wallclock provided in the share_info page, do you
suggest that dom0 should pass the wallclock value to Xen, so Xen can
initialize the shared_info wallclock time?

That would leave the fields uninitialized for dom0 which would be an
ABI change.

Thanks, Roger.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME
  2024-09-02  8:45     ` Roger Pau Monné
@ 2024-09-02  8:56       ` Jan Beulich
  0 siblings, 0 replies; 12+ messages in thread
From: Jan Beulich @ 2024-09-02  8:56 UTC (permalink / raw)
  To: Roger Pau Monné
  Cc: xen-devel, Daniel P. Smith, Marek Marczykowski-Górecki,
	Andrew Cooper

On 02.09.2024 10:45, Roger Pau Monné wrote:
> Is #PF the only fault that we can expect from EFI_GET_TIME?  That's
> what I've seen on some of the systems, but I wouldn't for example
> discard #GP or #UD as also possible outcomes?

I've definitely seen #GP in stack traces. What I can't say for sure is
whether those were _only_ because of our originally wrong stack
alignment, when firmware code wants to use XMM registers.

Jan



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper
  2024-08-30  9:52 ` [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper Roger Pau Monne
  2024-08-30 15:47   ` Andrew Cooper
@ 2024-09-03  6:24   ` Jan Beulich
  2024-09-03  7:35     ` Roger Pau Monné
  1 sibling, 1 reply; 12+ messages in thread
From: Jan Beulich @ 2024-09-03  6:24 UTC (permalink / raw)
  To: Roger Pau Monne
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Andrew Cooper,
	xen-devel

On 30.08.2024 11:52, Roger Pau Monne wrote:
> @@ -1285,33 +1270,56 @@ static unsigned long get_cmos_time(void)
>          } while ( (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) &&
>                    t2 < MILLISECS(3) );
>  
> -        __get_cmos_time(&rtc);
> +        __get_cmos_time(rtc);
>  
>          spin_unlock_irqrestore(&rtc_lock, flags);
>  
> -        if ( likely(!cmos_rtc_probe) ||
> -             t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
> -             rtc.sec >= 60 || rtc.min >= 60 || rtc.hour >= 24 ||
> -             !rtc.day || rtc.day > 31 ||
> -             !rtc.mon || rtc.mon > 12 )
> -            break;
> +        if ( likely(!cmos_rtc_probe) )
> +            return true;
> +
> +        if ( t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
> +             rtc->sec >= 60 || rtc->min >= 60 || rtc->hour >= 24 ||
> +             !rtc->day || rtc->day > 31 ||
> +             !rtc->mon || rtc->mon > 12 )
> +            return false;
>  
>          if ( seconds < 60 )
>          {
> -            if ( rtc.sec != seconds )
> -            {
> -                cmos_rtc_probe = false;

This clearing of the variable is lost, which looks wrong to me.

Jan

> +            if ( rtc->sec != seconds )
>                  acpi_gbl_FADT.boot_flags &= ~ACPI_FADT_NO_CMOS_RTC;
> -            }
> -            break;
> +            return true;
>          }
>  
>          process_pending_softirqs();
>  
> -        seconds = rtc.sec;
> +        seconds = rtc->sec;
>      }
>  
> -    if ( unlikely(cmos_rtc_probe) )
> +    ASSERT_UNREACHABLE();
> +    return false;
> +}
> +
> +static unsigned long get_cmos_time(void)
> +{
> +    struct rtc_time rtc;
> +    static bool __read_mostly cmos_rtc_probe;
> +    boolean_param("cmos-rtc-probe", cmos_rtc_probe);
> +
> +    if ( efi_enabled(EFI_RS) )
> +    {
> +        unsigned long res = efi_get_time();
> +
> +        if ( res )
> +            return res;
> +    }
> +
> +    if ( likely(!(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)) )
> +        cmos_rtc_probe = false;
> +    else if ( system_state < SYS_STATE_smp_boot && !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 ( unlikely(!read_cmos_time(&rtc, cmos_rtc_probe)) )
>          panic("No CMOS RTC found - system must be booted from EFI\n");
>  
>      return mktime(rtc.year, rtc.mon, rtc.day, rtc.hour, rtc.min, rtc.sec);



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper
  2024-09-03  6:24   ` Jan Beulich
@ 2024-09-03  7:35     ` Roger Pau Monné
  2024-09-03  7:53       ` Jan Beulich
  0 siblings, 1 reply; 12+ messages in thread
From: Roger Pau Monné @ 2024-09-03  7:35 UTC (permalink / raw)
  To: Jan Beulich
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Andrew Cooper,
	xen-devel

On Tue, Sep 03, 2024 at 08:24:18AM +0200, Jan Beulich wrote:
> On 30.08.2024 11:52, Roger Pau Monne wrote:
> > @@ -1285,33 +1270,56 @@ static unsigned long get_cmos_time(void)
> >          } while ( (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) &&
> >                    t2 < MILLISECS(3) );
> >  
> > -        __get_cmos_time(&rtc);
> > +        __get_cmos_time(rtc);
> >  
> >          spin_unlock_irqrestore(&rtc_lock, flags);
> >  
> > -        if ( likely(!cmos_rtc_probe) ||
> > -             t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
> > -             rtc.sec >= 60 || rtc.min >= 60 || rtc.hour >= 24 ||
> > -             !rtc.day || rtc.day > 31 ||
> > -             !rtc.mon || rtc.mon > 12 )
> > -            break;
> > +        if ( likely(!cmos_rtc_probe) )
> > +            return true;
> > +
> > +        if ( t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
> > +             rtc->sec >= 60 || rtc->min >= 60 || rtc->hour >= 24 ||
> > +             !rtc->day || rtc->day > 31 ||
> > +             !rtc->mon || rtc->mon > 12 )
> > +            return false;
> >  
> >          if ( seconds < 60 )
> >          {
> > -            if ( rtc.sec != seconds )
> > -            {
> > -                cmos_rtc_probe = false;
> 
> This clearing of the variable is lost, which looks wrong to me.

Note the code in get_cmos_time() is modified, so the variable is no
longer used past the call to read_cmos_time().  Instead the signaling
of whether the CMOS is functional or not is done using the return
value of the newly introduced read_cmos_time() function.

Thanks, Roger.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper
  2024-09-03  7:35     ` Roger Pau Monné
@ 2024-09-03  7:53       ` Jan Beulich
  2024-09-03  8:38         ` Roger Pau Monné
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Beulich @ 2024-09-03  7:53 UTC (permalink / raw)
  To: Roger Pau Monné
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Andrew Cooper,
	xen-devel

On 03.09.2024 09:35, Roger Pau Monné wrote:
> On Tue, Sep 03, 2024 at 08:24:18AM +0200, Jan Beulich wrote:
>> On 30.08.2024 11:52, Roger Pau Monne wrote:
>>> @@ -1285,33 +1270,56 @@ static unsigned long get_cmos_time(void)
>>>          } while ( (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) &&
>>>                    t2 < MILLISECS(3) );
>>>  
>>> -        __get_cmos_time(&rtc);
>>> +        __get_cmos_time(rtc);
>>>  
>>>          spin_unlock_irqrestore(&rtc_lock, flags);
>>>  
>>> -        if ( likely(!cmos_rtc_probe) ||
>>> -             t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
>>> -             rtc.sec >= 60 || rtc.min >= 60 || rtc.hour >= 24 ||
>>> -             !rtc.day || rtc.day > 31 ||
>>> -             !rtc.mon || rtc.mon > 12 )
>>> -            break;
>>> +        if ( likely(!cmos_rtc_probe) )
>>> +            return true;
>>> +
>>> +        if ( t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
>>> +             rtc->sec >= 60 || rtc->min >= 60 || rtc->hour >= 24 ||
>>> +             !rtc->day || rtc->day > 31 ||
>>> +             !rtc->mon || rtc->mon > 12 )
>>> +            return false;
>>>  
>>>          if ( seconds < 60 )
>>>          {
>>> -            if ( rtc.sec != seconds )
>>> -            {
>>> -                cmos_rtc_probe = false;
>>
>> This clearing of the variable is lost, which looks wrong to me.
> 
> Note the code in get_cmos_time() is modified, so the variable is no
> longer used past the call to read_cmos_time().  Instead the signaling
> of whether the CMOS is functional or not is done using the return
> value of the newly introduced read_cmos_time() function.

I wasn't concerned of the further processing on the 1st invocation, but
of the behavior of the 2nd invocation. But yes, there the flag will end
up being cleared because of the FADT flag also having been cleared. Not
easily visible, though. Could minimally do with a remark in the
description.

Jan


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper
  2024-09-03  7:53       ` Jan Beulich
@ 2024-09-03  8:38         ` Roger Pau Monné
  0 siblings, 0 replies; 12+ messages in thread
From: Roger Pau Monné @ 2024-09-03  8:38 UTC (permalink / raw)
  To: Jan Beulich
  Cc: Daniel P. Smith, Marek Marczykowski-Górecki, Andrew Cooper,
	xen-devel

On Tue, Sep 03, 2024 at 09:53:47AM +0200, Jan Beulich wrote:
> On 03.09.2024 09:35, Roger Pau Monné wrote:
> > On Tue, Sep 03, 2024 at 08:24:18AM +0200, Jan Beulich wrote:
> >> On 30.08.2024 11:52, Roger Pau Monne wrote:
> >>> @@ -1285,33 +1270,56 @@ static unsigned long get_cmos_time(void)
> >>>          } while ( (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) &&
> >>>                    t2 < MILLISECS(3) );
> >>>  
> >>> -        __get_cmos_time(&rtc);
> >>> +        __get_cmos_time(rtc);
> >>>  
> >>>          spin_unlock_irqrestore(&rtc_lock, flags);
> >>>  
> >>> -        if ( likely(!cmos_rtc_probe) ||
> >>> -             t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
> >>> -             rtc.sec >= 60 || rtc.min >= 60 || rtc.hour >= 24 ||
> >>> -             !rtc.day || rtc.day > 31 ||
> >>> -             !rtc.mon || rtc.mon > 12 )
> >>> -            break;
> >>> +        if ( likely(!cmos_rtc_probe) )
> >>> +            return true;
> >>> +
> >>> +        if ( t1 > SECONDS(1) || t2 >= MILLISECS(3) ||
> >>> +             rtc->sec >= 60 || rtc->min >= 60 || rtc->hour >= 24 ||
> >>> +             !rtc->day || rtc->day > 31 ||
> >>> +             !rtc->mon || rtc->mon > 12 )
> >>> +            return false;
> >>>  
> >>>          if ( seconds < 60 )
> >>>          {
> >>> -            if ( rtc.sec != seconds )
> >>> -            {
> >>> -                cmos_rtc_probe = false;
> >>
> >> This clearing of the variable is lost, which looks wrong to me.
> > 
> > Note the code in get_cmos_time() is modified, so the variable is no
> > longer used past the call to read_cmos_time().  Instead the signaling
> > of whether the CMOS is functional or not is done using the return
> > value of the newly introduced read_cmos_time() function.
> 
> I wasn't concerned of the further processing on the 1st invocation, but
> of the behavior of the 2nd invocation. But yes, there the flag will end
> up being cleared because of the FADT flag also having been cleared. Not
> easily visible, though. Could minimally do with a remark in the
> description.

Indeed, the variable gets clearer on further invocations, as the
adjustment to ACPI_FADT_NO_CMOS_RTC gets propagated.

Given Andrew comments, I've reworked all of this and I think it ended
up being clearer.

Thanks, Roger.


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2024-09-03  8:38 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-30  9:52 [PATCH v2 0/2] x86/time: prefer CMOS over EFI_GET_TIME Roger Pau Monne
2024-08-30  9:52 ` [PATCH v2 1/2] x86/time: split CMOS time fetching into wrapper Roger Pau Monne
2024-08-30 15:47   ` Andrew Cooper
2024-09-03  6:24   ` Jan Beulich
2024-09-03  7:35     ` Roger Pau Monné
2024-09-03  7:53       ` Jan Beulich
2024-09-03  8:38         ` Roger Pau Monné
2024-08-30  9:52 ` [PATCH v2 2/2] x86/time: prefer CMOS over EFI_GET_TIME Roger Pau Monne
2024-08-30 16:31   ` Andrew Cooper
2024-09-02  8:13     ` Jan Beulich
2024-09-02  8:45     ` Roger Pau Monné
2024-09-02  8:56       ` 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.