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

Re: [PATCH 4/4] xen/uart: enable parsing ACPI SPCR on x86


  • To: Roger Pau Monne <roger.pau@xxxxxxxxxx>
  • From: Jan Beulich <jbeulich@xxxxxxxx>
  • Date: Thu, 26 Mar 2026 13:45:51 +0100
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=google header.d=suse.com header.i="@suse.com" header.h="Content-Transfer-Encoding:In-Reply-To:Autocrypt:From:Content-Language:References:Cc:To:Subject:User-Agent:MIME-Version:Date:Message-ID"
  • Autocrypt: addr=jbeulich@xxxxxxxx; keydata= xsDiBFk3nEQRBADAEaSw6zC/EJkiwGPXbWtPxl2xCdSoeepS07jW8UgcHNurfHvUzogEq5xk hu507c3BarVjyWCJOylMNR98Yd8VqD9UfmX0Hb8/BrA+Hl6/DB/eqGptrf4BSRwcZQM32aZK 7Pj2XbGWIUrZrd70x1eAP9QE3P79Y2oLrsCgbZJfEwCgvz9JjGmQqQkRiTVzlZVCJYcyGGsD /0tbFCzD2h20ahe8rC1gbb3K3qk+LpBtvjBu1RY9drYk0NymiGbJWZgab6t1jM7sk2vuf0Py O9Hf9XBmK0uE9IgMaiCpc32XV9oASz6UJebwkX+zF2jG5I1BfnO9g7KlotcA/v5ClMjgo6Gl MDY4HxoSRu3i1cqqSDtVlt+AOVBJBACrZcnHAUSuCXBPy0jOlBhxPqRWv6ND4c9PH1xjQ3NP nxJuMBS8rnNg22uyfAgmBKNLpLgAGVRMZGaGoJObGf72s6TeIqKJo/LtggAS9qAUiuKVnygo 3wjfkS9A3DRO+SpU7JqWdsveeIQyeyEJ/8PTowmSQLakF+3fote9ybzd880fSmFuIEJldWxp Y2ggPGpiZXVsaWNoQHN1c2UuY29tPsJgBBMRAgAgBQJZN5xEAhsDBgsJCAcDAgQVAggDBBYC AwECHgECF4AACgkQoDSui/t3IH4J+wCfQ5jHdEjCRHj23O/5ttg9r9OIruwAn3103WUITZee e7Sbg12UgcQ5lv7SzsFNBFk3nEQQCACCuTjCjFOUdi5Nm244F+78kLghRcin/awv+IrTcIWF hUpSs1Y91iQQ7KItirz5uwCPlwejSJDQJLIS+QtJHaXDXeV6NI0Uef1hP20+y8qydDiVkv6l IreXjTb7DvksRgJNvCkWtYnlS3mYvQ9NzS9PhyALWbXnH6sIJd2O9lKS1Mrfq+y0IXCP10eS FFGg+Av3IQeFatkJAyju0PPthyTqxSI4lZYuJVPknzgaeuJv/2NccrPvmeDg6Coe7ZIeQ8Yj t0ARxu2xytAkkLCel1Lz1WLmwLstV30g80nkgZf/wr+/BXJW/oIvRlonUkxv+IbBM3dX2OV8 AmRv1ySWPTP7AAMFB/9PQK/VtlNUJvg8GXj9ootzrteGfVZVVT4XBJkfwBcpC/XcPzldjv+3 HYudvpdNK3lLujXeA5fLOH+Z/G9WBc5pFVSMocI71I8bT8lIAzreg0WvkWg5V2WZsUMlnDL9 mpwIGFhlbM3gfDMs7MPMu8YQRFVdUvtSpaAs8OFfGQ0ia3LGZcjA6Ik2+xcqscEJzNH+qh8V m5jjp28yZgaqTaRbg3M/+MTbMpicpZuqF4rnB0AQD12/3BNWDR6bmh+EkYSMcEIpQmBM51qM EKYTQGybRCjpnKHGOxG0rfFY1085mBDZCH5Kx0cl0HVJuQKC+dV2ZY5AqjcKwAxpE75MLFkr wkkEGBECAAkFAlk3nEQCGwwACgkQoDSui/t3IH7nnwCfcJWUDUFKdCsBH/E5d+0ZnMQi+G0A nAuWpQkjM1ASeQwSHEeAWPgskBQL
  • Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Anthony PERARD <anthony.perard@xxxxxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Julien Grall <julien@xxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, xen-devel@xxxxxxxxxxxxxxxxxxxx
  • Delivery-date: Thu, 26 Mar 2026 12:45:57 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

On 25.03.2026 15:58, Roger Pau Monne wrote:
> Introduce extra logic to allow parsing ACPI tables extra early, and use it
> to parse the ACPI SPCR table and obtain the serial configuration.
> 
> This is gated to the "acpi" device type being set in "com1" on the Xen
> command line.  Note that there can only be one serial device described in
> the SPCR, so limit it's usage to com1 exclusively for the time being.
> 
> I can't test the interrupt information parsing on my system, as the
> interrupt is set to GSI with a value of 0xff, which is outside of the range
> of GSIs available on the system.  I've also assumed that the interrupt
> being 0xff is used to signal not interrupt setup (just like the Interrupt
> Pin register on PCI headers).
> 
> Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
> ---
> WIP/RFC, not sure whether there's interest in attempting to pursue this
> further on x86.  So far the device I have is also exposed on the PCI bus
> aside from SPCR, so using com1=device=amt also works to detect it.
> 
> Posting it kind of early to know whether I should try to polish it for
> submission or we are happy with not having this on x86.

One concern of mine is the altering ACPI CA code. Otoh, seeing how early
you need this for SPCR, I wonder if it then couldn't also be used for the
BGRT work that's being done in parallel.

> @@ -523,3 +540,67 @@ acpi_tb_parse_root_table(acpi_physical_address 
> rsdp_address, u8 flags)
>  
>       return_ACPI_STATUS(AE_OK);
>  }
> +
> +acpi_status __init
> +acpi_early_get_table(const char *signature, acpi_native_uint instance,
> +                     struct acpi_table_header **out_table)
> +{
> +     static acpi_physical_address __initdata table_addr[128];
> +     static unsigned int __initdata table_count;
> +     static unsigned int __initdata table_entry_size;
> +     unsigned int i;
> +
> +     ACPI_FUNCTION_TRACE(tb_early_get_table);
> +
> +     if (!table_count) {
> +             struct acpi_table_header *table;
> +             void *table_entry;
> +             acpi_status status;
> +             acpi_physical_address rsdp_address = acpi_os_get_root_pointer();
> +
> +             if (!rsdp_address)
> +                     return_ACPI_STATUS(AE_NOT_FOUND);
> +
> +             status = acpi_tb_get_root_table(rsdp_address, &table,
> +                                             &table_entry_size);
> +             if (!ACPI_SUCCESS(status))
> +                     return_ACPI_STATUS(status);
> +
> +             /* Calculate the number of tables described in the root table */
> +             table_count = (table->length - sizeof(*table)) / 
> table_entry_size;
> +
> +             if (table_count > ARRAY_SIZE(table_addr)) {
> +                     table_count = 0;
> +                     return_ACPI_STATUS(AE_NO_MEMORY);
> +             }

Rather than failing, limit table_count to ARRAY_SIZE()?

> --- a/xen/drivers/char/ns16550.c
> +++ b/xen/drivers/char/ns16550.c
> @@ -1375,6 +1375,136 @@ static void enable_exar_enhanced_bits(const struct 
> ns16550 *uart)
>  
>  #endif /* CONFIG_HAS_PCI */
>  
> +#ifdef CONFIG_ACPI
> +#include <acpi/acpi.h>

With xen/acpi.h included (below) this shouldn't be needed.

> +#include <acpi/actables.h>
> +
> +#include <xen/acpi.h>
> +
> +static int __init acpi_uart_config(struct ns16550 *uart, unsigned int idx)
> +{
> +    struct acpi_table_header *table;

While this can't be pointer-to-const, ...

> +    struct acpi_table_spcr *spcr;

... it looks like this can be.

> +    acpi_status status;
> +    int rc = 0;
> +
> +    /*
> +     * SPCR specifies a single port, expect it to be configured at position 0
> +     * in the uart array.
> +     */
> +    if ( idx )
> +        return -EXDEV;

While this matches what ns16550_acpi_uart_init() does / wants, I'm not sure
this is a good idea. If a system had a normal COM1 and something in SPCR,
one would need to re-define COM2 with the COM1 settings.

> +    if ( system_state <= SYS_STATE_early_boot )
> +        status = acpi_early_get_table(ACPI_SIG_SPCR, 0, &table);
> +    else
> +        status = acpi_get_table(ACPI_SIG_SPCR, 0, &table);
> +
> +    if ( ACPI_FAILURE(status) )
> +    {
> +        printk(XENLOG_ERR "Failed to find or parse ACPI SPCR table\n");
> +        return -ENODEV;
> +    }
> +
> +    spcr = container_of(table, struct acpi_table_spcr, header);
> +
> +    rc = -EDOM;
> +    if ( spcr->interface_type != ACPI_DBG2_16550_COMPATIBLE )
> +    {
> +        printk(XENLOG_ERR "Incompatible ACPI SPCR UART interface %u\n",
> +               spcr->interface_type);
> +        goto out;
> +    }
> +
> +    if ( spcr->serial_port.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
> +         (IS_ENABLED(CONFIG_ARM) ||

Better !IS_ENABLED(CONFIG_X86), seeing how neither RISC-V nor PPC have
I/O ports?

> +          spcr->serial_port.space_id != ACPI_ADR_SPACE_SYSTEM_IO) )
> +    {
> +        printk(XENLOG_ERR "Incompatible ACPI SPCR UART address space %u\n",
> +               spcr->serial_port.space_id);
> +        goto out;
> +    }
> +
> +    if ( !spcr->serial_port.address )
> +    {
> +        printk(XENLOG_ERR "ACPI SPCR console redirection disabled\n");
> +        goto out;
> +    }
> +
> +    uart->io_base = spcr->serial_port.address;

Elsewhere we assume MMIO if the address is 0x10000 or above. Here we have
an ACPI_ADR_SPACE_* indicator, which I think we should take into account.
For now merely to reject values not fitting assumptions elsewhere, I guess.

> +    uart->io_size = DIV_ROUND_UP(spcr->serial_port.bit_width, BITS_PER_BYTE);
> +    uart->reg_shift = spcr->serial_port.bit_offset;
> +
> +    uart->parity = spcr->parity;
> +    uart->stop_bits = spcr->stop_bits;
> +    uart->data_bits = 8;
> +
> +    if ( uart->baud == BAUD_AUTO && spcr->baud_rate )
> +    {
> +        switch ( spcr->baud_rate )
> +        {
> +        case ACPI_SPCR_BAUD_9600:
> +            uart->baud = 9600;
> +            break;
> +
> +        case ACPI_SPCR_BAUD_19200:
> +            uart->baud = 19200;
> +            break;
> +
> +        case ACPI_SPCR_BAUD_57600:
> +            uart->baud = 57600;
> +            break;
> +
> +        case ACPI_SPCR_BAUD_115200:
> +            uart->baud = 115200;
> +            break;
> +
> +        default:
> +            printk(XENLOG_WARNING
> +                   "Ignoring invalid baud rate %u in ACPI SPCR\n",
> +                   spcr->baud_rate);

Maybe better s/invalid/unknown/?

Also, please add "break" for Misra's sake.

> +        }
> +    }
> +
> +    if ( IS_ENABLED(CONFIG_X86) )
> +    {
> +        /* Use polling mode by default. */
> +        uart->irq = 0;
> +
> +        if ( (spcr->interrupt_type & ACPI_SPCR_INTR_TYPE_IO_APIC) &&
> +             spcr->interrupt < 0xff )
> +            uart->irq = spcr->interrupt;
> +        else if ( (spcr->interrupt_type & ACPI_SPCR_INTR_TYPE_PC_AT) &&
> +                  ((spcr->pc_interrupt >=  2 && spcr->pc_interrupt <=  7) ||

Is 2 valid to use? That's the cascade in 8259-s.

> +                   (spcr->pc_interrupt >= 14 && spcr->pc_interrupt <= 15)) )
> +            uart->irq = spcr->pc_interrupt;
> +    }
> +
> +#ifdef CONFIG_ARM
> +    /* The trigger/polarity information is not available in spcr. */
> +    irq_set_type(spcr->interrupt, IRQ_TYPE_LEVEL_HIGH);
> +    uart->irq = spcr->interrupt;
> +#endif /* CONFIG_ARM */
> +
> +#ifdef CONFIG_HAS_PCI
> +    if ( spcr->pci_device_id != 0xffff && spcr->pci_vendor_id != 0xffff )
> +    {
> +        uart->ps_bdf_enable = true;
> +        uart->pci_device = PCI_SBDF(spcr->pci_segment, spcr->pci_bus,
> +                                    spcr->pci_device, spcr->pci_function);
> +    }
> +#endif /* CONFIG_HAS_PCI */
> +
> +    rc = 0;
> +
> + out:
> +    if ( system_state <= SYS_STATE_early_boot )
> +        acpi_os_unmap_memory(&spcr, spcr->header.length);

I think you'd better unmap "table" here, and I don't think the & is correct
to use.

> @@ -1643,8 +1782,17 @@ static bool __init parse_namevalue_pairs(char *str, 
> struct ns16550 *uart)
>              uart->reg_width = simple_strtoul(param_value, NULL, 0);
>              break;
>  
> -#ifdef CONFIG_HAS_PCI
>          case device:
> +#ifdef CONFIG_ACPI
> +            if ( strncmp(param_value, "acpi", 3) == 0 )
> +            {
> +                acpi_uart_config(uart, uart - ns16550_com);
> +                dev_set = true;
> +                break;
> +            }
> +            else

May I ask to drop either the "else" or the "break"? To match PCI code,
it would be the latter.

> +#endif /* CONFIG_ACPI */
> +#ifdef CONFIG_HAS_PCI
>              if ( strncmp(param_value, "pci", 3) == 0 )
>              {
>                  pci_uart_config(uart, 1/* skip AMT */, uart - ns16550_com);
> @@ -1656,9 +1804,11 @@ static bool __init parse_namevalue_pairs(char *str, 
> struct ns16550 *uart)
>                  dev_set = true;
>              }
>              else
> +#endif /* CONFIG_HAS_PCI */
>                  PARSE_ERR_RET("Unknown device type %s\n", param_value);
>              break;

I think for !ACPI && !HAS_PCI we'd better not alter behavior (i.e. that
case would still better end up at default:).

> --- a/xen/include/acpi/actbl2.h
> +++ b/xen/include/acpi/actbl2.h
> @@ -1037,6 +1037,16 @@ struct acpi_table_spcr {
>  
>  #define ACPI_SPCR_DO_NOT_DISABLE    (1)
>  
> +/* Masks for interrupt_type field above */
> +#define ACPI_SPCR_INTR_TYPE_PC_AT   0x01
> +#define ACPI_SPCR_INTR_TYPE_IO_APIC 0x02
> +
> +/* Values for the baud_rate field above */
> +#define ACPI_SPCR_BAUD_9600         3
> +#define ACPI_SPCR_BAUD_19200        4
> +#define ACPI_SPCR_BAUD_57600        5
> +#define ACPI_SPCR_BAUD_115200       7

Not your fault, but I wonder why we have SPCR here when Linux has it in
actbl3.h.

Jan



 


Rackspace

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