[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v3 1/1] x86/acpi: Use FADT flags to determine the PMTMR width



On Fri, Jun 19, 2020 at 10:00:16PM +0000, Grzegorz Uriasz wrote:
> On some computers the bit width of the PM Timer as reported
> by ACPI is 32 bits when in fact the FADT flags report correctly
> that the timer is 24 bits wide. On affected machines such as the
> ASUS FX504GM and never gaming laptops this results in the inability
> to resume the machine from suspend. Without this patch suspend is
> broken on affected machines and even if a machine manages to resume
> correctly then the kernel time and xen timers are trashed.
> 
> Signed-off-by: Grzegorz Uriasz <gorbak25@xxxxxxxxx>
> ---
> Cc: Jan Beulich <jbeulich@xxxxxxxx>
> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
> Cc: Wei Liu <wl@xxxxxxx>
> Cc: "Roger Pau Monné" <roger.pau@xxxxxxxxxx>
> Cc: marmarek@xxxxxxxxxxxxxxxxxxxxxx
> Cc: contact@xxxxxxxxxxxx
> Cc: jakub@xxxxxxxxxxx
> Cc: j.nowak26@xxxxxxxxxxxxxxxxx
> 
> Changes in v2:
> - Check pm timer access width
> - Proper timer width is set when xpm block is not present
> - Cleanup timer initialization
> 
> Changes in v3:
> - Check pm timer bit offset
> - Warn about acpi firmware bugs
> - Drop int cast in init_pmtimer
> - Merge if's in init_pmtimer
> ---
>  xen/arch/x86/acpi/boot.c    | 19 +++++++++++++++----
>  xen/arch/x86/time.c         | 12 ++++--------
>  xen/include/acpi/acmacros.h |  8 ++++++++
>  3 files changed, 27 insertions(+), 12 deletions(-)
> 
> diff --git a/xen/arch/x86/acpi/boot.c b/xen/arch/x86/acpi/boot.c
> index bcba52e232..24fd236eb5 100644
> --- a/xen/arch/x86/acpi/boot.c
> +++ b/xen/arch/x86/acpi/boot.c
> @@ -475,10 +475,17 @@ static int __init acpi_parse_fadt(struct 
> acpi_table_header *table)
>  
>  #ifdef CONFIG_X86_PM_TIMER
>       /* detect the location of the ACPI PM Timer */
> -     if (fadt->header.revision >= FADT2_REVISION_ID) {
> +     if (fadt->header.revision >= FADT2_REVISION_ID &&
> +         fadt->xpm_timer_block.space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
>               /* FADT rev. 2 */
> -             if (fadt->xpm_timer_block.space_id ==
> -                 ACPI_ADR_SPACE_SYSTEM_IO) {
> +             if (fadt->xpm_timer_block.access_width != 0 &&
> +                 ACPI_ACCESS_BIT_WIDTH(fadt->xpm_timer_block.access_width) 
> != 32)
> +                     printk(KERN_WARNING PREFIX "PM-Timer has invalid access 
> width(%u)\n",
> +                            fadt->xpm_timer_block.access_width);
> +             else if (fadt->xpm_timer_block.bit_offset != 0)

This should be a plain if instead of an else if, it's possible the
block has both the access_width and the bit_offset wrong.

> +                     printk(KERN_WARNING PREFIX "PM-Timer has invalid bit 
> offset(%u)\n",
> +                            fadt->xpm_timer_block.bit_offset);
> +             else {
>                       pmtmr_ioport = fadt->xpm_timer_block.address;
>                       pmtmr_width = fadt->xpm_timer_block.bit_width;
>               }
> @@ -490,8 +497,12 @@ static int __init acpi_parse_fadt(struct 
> acpi_table_header *table)
>        */
>       if (!pmtmr_ioport) {
>               pmtmr_ioport = fadt->pm_timer_block;
> -             pmtmr_width = fadt->pm_timer_length == 4 ? 24 : 0;
> +             pmtmr_width = fadt->pm_timer_length == 4 ? 32 : 0;
>       }
> +     if (pmtmr_width < 32 && fadt->flags & ACPI_FADT_32BIT_TIMER)
> +        printk(KERN_WARNING PREFIX "PM-Timer is too short\n");

Indentation. Also this should be a fatal error likely? You should set
pmtmr_ioport = 0?

> +     if (pmtmr_width > 24 && !(fadt->flags & ACPI_FADT_32BIT_TIMER))
> +             pmtmr_width = 24;
>       if (pmtmr_ioport)
>               printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x (%u bits)\n",
>                      pmtmr_ioport, pmtmr_width);
> diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
> index d643590c0a..8a45514908 100644
> --- a/xen/arch/x86/time.c
> +++ b/xen/arch/x86/time.c
> @@ -457,16 +457,13 @@ static u64 read_pmtimer_count(void)
>  static s64 __init init_pmtimer(struct platform_timesource *pts)
>  {
>      u64 start;
> -    u32 count, target, mask = 0xffffff;
> +    u32 count, target, mask;
>  
> -    if ( !pmtmr_ioport || !pmtmr_width )
> +    if ( !pmtmr_ioport || (pmtmr_width != 24 && pmtmr_width != 32) )
>          return 0;
>  
> -    if ( pmtmr_width == 32 )
> -    {
> -        pts->counter_bits = 32;
> -        mask = 0xffffffff;
> -    }
> +    pts->counter_bits = pmtmr_width;
> +    mask = (1ull << pmtmr_width) - 1;
>  
>      count = inl(pmtmr_ioport) & mask;
>      start = rdtsc_ordered();
> @@ -486,7 +483,6 @@ static struct platform_timesource __initdata plt_pmtimer =
>      .name = "ACPI PM Timer",
>      .frequency = ACPI_PM_FREQUENCY,
>      .read_counter = read_pmtimer_count,
> -    .counter_bits = 24,
>      .init = init_pmtimer
>  };
>  
> diff --git a/xen/include/acpi/acmacros.h b/xen/include/acpi/acmacros.h
> index 6765535053..86c503c20f 100644
> --- a/xen/include/acpi/acmacros.h
> +++ b/xen/include/acpi/acmacros.h
> @@ -121,6 +121,14 @@
>  #define ACPI_COMPARE_NAME(a,b)          (!ACPI_STRNCMP (ACPI_CAST_PTR 
> (char,(a)), ACPI_CAST_PTR (char,(b)), ACPI_NAME_SIZE))
>  #endif
>  
> +/*
> + * Algorithm to obtain access bit or byte width.
> + * Can be used with access_width of struct acpi_generic_address and 
> access_size of
> + * struct acpi_resource_generic_register.
> + */
> +#define ACPI_ACCESS_BIT_WIDTH(size)     (1 << ((size) + 2))
> +#define ACPI_ACCESS_BYTE_WIDTH(size)    (1 << ((size) - 1))

Note sure I would introduce this last one, since it's not used by the
code.

Thanks, Roger.



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.