diff mbox series

[1/1] tty: serial: handle HAS_IOPORT dependencies

Message ID 20240405152924.252598-2-schnelle@linux.ibm.com
State New
Headers show
Series tty: Handle HAS_IOPORT dependencies | expand

Commit Message

Niklas Schnelle April 5, 2024, 3:29 p.m. UTC
In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
compile time. We thus need to add HAS_IOPORT as dependency for those
drivers using them unconditionally. For 8250 based drivers some support
MMIO only use so fence only the parts requiring I/O ports.

Co-developed-by: Arnd Bergmann <arnd@kernel.org>
Signed-off-by: Arnd Bergmann <arnd@kernel.org>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
---
Note: This patch does not depend any not-yet-mainline HAS_IOPORT changes
and may be merged via subsystem specific trees at your earliest
convenience.

Note 2: This was previously acked here:
https://lore.kernel.org/all/2023053050-prodigal-shine-4d1c@gregkh/
Given this was almost a year ago and didn't apply then I didn't
carry the Ack over though.

 drivers/tty/Kconfig                  |  4 +--
 drivers/tty/serial/8250/8250_early.c |  4 +++
 drivers/tty/serial/8250/8250_pci.c   | 14 ++++++++++
 drivers/tty/serial/8250/8250_port.c  | 42 +++++++++++++++++++++++-----
 drivers/tty/serial/8250/Kconfig      |  7 ++---
 drivers/tty/serial/Kconfig           |  2 +-
 6 files changed, 59 insertions(+), 14 deletions(-)

Comments

Ilpo Järvinen April 8, 2024, 9:54 a.m. UTC | #1
On Fri, 5 Apr 2024, Niklas Schnelle wrote:

> In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
> compile time. We thus need to add HAS_IOPORT as dependency for those
> drivers using them unconditionally. For 8250 based drivers some support
> MMIO only use so fence only the parts requiring I/O ports.
> 
> Co-developed-by: Arnd Bergmann <arnd@kernel.org>
> Signed-off-by: Arnd Bergmann <arnd@kernel.org>
> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> ---
> Note: This patch does not depend any not-yet-mainline HAS_IOPORT changes
> and may be merged via subsystem specific trees at your earliest
> convenience.
> 
> Note 2: This was previously acked here:
> https://lore.kernel.org/all/2023053050-prodigal-shine-4d1c@gregkh/
> Given this was almost a year ago and didn't apply then I didn't
> carry the Ack over though.
> 
>  drivers/tty/Kconfig                  |  4 +--
>  drivers/tty/serial/8250/8250_early.c |  4 +++
>  drivers/tty/serial/8250/8250_pci.c   | 14 ++++++++++
>  drivers/tty/serial/8250/8250_port.c  | 42 +++++++++++++++++++++++-----
>  drivers/tty/serial/8250/Kconfig      |  7 ++---
>  drivers/tty/serial/Kconfig           |  2 +-
>  6 files changed, 59 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
> index a45d423ad10f..63a494d36a1f 100644
> --- a/drivers/tty/Kconfig
> +++ b/drivers/tty/Kconfig
> @@ -220,7 +220,7 @@ config MOXA_INTELLIO
>  
>  config MOXA_SMARTIO
>  	tristate "Moxa SmartIO support v. 2.0"
> -	depends on SERIAL_NONSTANDARD && PCI
> +	depends on SERIAL_NONSTANDARD && PCI && HAS_IOPORT
>  	help
>  	  Say Y here if you have a Moxa SmartIO multiport serial card and/or
>  	  want to help develop a new version of this driver.
> @@ -302,7 +302,7 @@ config GOLDFISH_TTY_EARLY_CONSOLE
>  
>  config IPWIRELESS
>  	tristate "IPWireless 3G UMTS PCMCIA card support"
> -	depends on PCMCIA && NETDEVICES
> +	depends on PCMCIA && NETDEVICES && HAS_IOPORT
>  	select PPP
>  	help
>  	  This is a driver for 3G UMTS PCMCIA card from IPWireless company. In
> diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
> index e3f482fd3de4..8f9aec2e7c7d 100644
> --- a/drivers/tty/serial/8250/8250_early.c
> +++ b/drivers/tty/serial/8250/8250_early.c
> @@ -46,8 +46,10 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset)
>  		return readl(port->membase + offset);
>  	case UPIO_MEM32BE:
>  		return ioread32be(port->membase + offset);
> +#ifdef CONFIG_HAS_IOPORT
>  	case UPIO_PORT:
>  		return inb(port->iobase + offset);
> +#endif
>  	default:
>  		return 0;
>  	}
> @@ -70,9 +72,11 @@ static void serial8250_early_out(struct uart_port *port, int offset, int value)
>  	case UPIO_MEM32BE:
>  		iowrite32be(value, port->membase + offset);
>  		break;
> +#ifdef CONFIG_HAS_IOPORT
>  	case UPIO_PORT:
>  		outb(value, port->iobase + offset);
>  		break;
> +#endif
>  	}
>  }
>  
> diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
> index 0d35c77fad9e..38ac5236d2ea 100644
> --- a/drivers/tty/serial/8250/8250_pci.c
> +++ b/drivers/tty/serial/8250/8250_pci.c
> @@ -928,6 +928,7 @@ static int pci_netmos_init(struct pci_dev *dev)
>  	return num_serial;
>  }
>  
> +#ifdef CONFIG_HAS_IOPORT
>  /*
>   * These chips are available with optionally one parallel port and up to
>   * two serial ports. Unfortunately they all have the same product id.
> @@ -1054,6 +1055,7 @@ static void pci_ite887x_exit(struct pci_dev *dev)
>  	ioport &= 0xffff;
>  	release_region(ioport, ITE_887x_IOSIZE);
>  }
> +#endif /* CONFIG_HAS_IOPORT */
>  
>  /*
>   * Oxford Semiconductor Inc.
> @@ -1328,6 +1330,7 @@ static int pci_oxsemi_tornado_setup(struct serial_private *priv,
>  #define QOPR_CLOCK_X8		0x0003
>  #define QOPR_CLOCK_RATE_MASK	0x0003
>  
> +#ifdef CONFIG_HAS_IOPORT
>  /* Quatech devices have their own extra interface features */
>  static struct pci_device_id quatech_cards[] = {
>  	{ PCI_DEVICE_DATA(QUATECH, QSC100,   1) },
> @@ -1547,6 +1550,7 @@ static int pci_quatech_setup(struct serial_private *priv,
>  		pci_warn(priv->dev, "software control of RS422 features not currently supported.\n");
>  	return pci_default_setup(priv, board, port, idx);
>  }
> +#endif /* CONFIG_HAS_IOPORT */
>  
>  static int pci_default_setup(struct serial_private *priv,
>  		  const struct pciserial_board *board,
> @@ -1826,6 +1830,7 @@ static int skip_tx_en_setup(struct serial_private *priv,
>  	return pci_default_setup(priv, board, port, idx);
>  }
>  
> +#ifdef CONFIG_HAS_IOPORT
>  static void kt_handle_break(struct uart_port *p)
>  {
>  	struct uart_8250_port *up = up_to_u8250p(p);
> @@ -1869,6 +1874,7 @@ static int kt_serial_setup(struct serial_private *priv,
>  	port->port.handle_break = kt_handle_break;
>  	return skip_tx_en_setup(priv, board, port, idx);
>  }
> +#endif /* CONFIG_HAS_IOPORT */
>  
>  static int pci_eg20t_init(struct pci_dev *dev)
>  {
> @@ -1913,6 +1919,7 @@ pci_wch_ch38x_setup(struct serial_private *priv,
>  #define CH384_XINT_ENABLE_REG   0xEB
>  #define CH384_XINT_ENABLE_BIT   0x02
>  
> +#ifdef CONFIG_HAS_IOPORT
>  static int pci_wch_ch38x_init(struct pci_dev *dev)
>  {
>  	int max_port;
> @@ -1940,6 +1947,7 @@ static void pci_wch_ch38x_exit(struct pci_dev *dev)
>  	iobase = pci_resource_start(dev, 0);
>  	outb(0x0, iobase + CH384_XINT_ENABLE_REG);
>  }
> +#endif /* CONFIG_HAS_IOPORT */
>  
>  
>  static int
> @@ -2171,6 +2179,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
>  		.subdevice	= PCI_ANY_ID,
>  		.setup		= ce4100_serial_setup,
>  	},
> +#ifdef CONFIG_HAS_IOPORT
>  	{
>  		.vendor		= PCI_VENDOR_ID_INTEL,
>  		.device		= PCI_DEVICE_ID_INTEL_PATSBURG_KT,
> @@ -2190,6 +2199,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
>  		.setup		= pci_default_setup,
>  		.exit		= pci_ite887x_exit,
>  	},
> +#endif
>  	/*
>  	 * National Instruments
>  	 */
> @@ -2311,6 +2321,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
>  		.exit		= pci_ni8430_exit,
>  	},
>  	/* Quatech */
> +#ifdef CONFIG_HAS_IOPORT
>  	{
>  		.vendor		= PCI_VENDOR_ID_QUATECH,
>  		.device		= PCI_ANY_ID,
> @@ -2319,6 +2330,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
>  		.init		= pci_quatech_init,
>  		.setup		= pci_quatech_setup,
>  	},
> +#endif
>  	/*
>  	 * Panacom
>  	 */
> @@ -2836,6 +2848,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
>  		.subdevice      = PCI_ANY_ID,
>  		.setup          = pci_wch_ch38x_setup,
>  	},
> +#ifdef CONFIG_HAS_IOPORT
>  	/* WCH CH384 8S card (16850 clone) */
>  	{
>  		.vendor         = PCIE_VENDOR_ID_WCH,
> @@ -2846,6 +2859,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
>  		.exit		= pci_wch_ch38x_exit,
>  		.setup          = pci_wch_ch38x_setup,
>  	},
> +#endif
>  	/*
>  	 * Broadcom TruManage (NetXtreme)
>  	 */
> diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
> index fc9dd5d45295..1c5e39c1a469 100644
> --- a/drivers/tty/serial/8250/8250_port.c
> +++ b/drivers/tty/serial/8250/8250_port.c
> @@ -338,6 +338,7 @@ static void default_serial_dl_write(struct uart_8250_port *up, u32 value)
>  	serial_out(up, UART_DLM, value >> 8 & 0xff);
>  }
>  
> +#ifdef CONFIG_HAS_IOPORT
>  static unsigned int hub6_serial_in(struct uart_port *p, int offset)
>  {
>  	offset = offset << p->regshift;
> @@ -351,6 +352,7 @@ static void hub6_serial_out(struct uart_port *p, int offset, int value)
>  	outb(p->hub6 - 1 + offset, p->iobase);
>  	outb(value, p->iobase + 1);
>  }
> +#endif /* CONFIG_HAS_IOPORT */
>  
>  static unsigned int mem_serial_in(struct uart_port *p, int offset)
>  {
> @@ -400,6 +402,7 @@ static unsigned int mem32be_serial_in(struct uart_port *p, int offset)
>  	return ioread32be(p->membase + offset);
>  }
>  
> +#ifdef CONFIG_HAS_IOPORT
>  static unsigned int io_serial_in(struct uart_port *p, int offset)
>  {
>  	offset = offset << p->regshift;
> @@ -411,6 +414,24 @@ static void io_serial_out(struct uart_port *p, int offset, int value)
>  	offset = offset << p->regshift;
>  	outb(value, p->iobase + offset);
>  }
> +#endif
> +static unsigned int no_serial_in(struct uart_port *p, int offset)
> +{
> +	return (unsigned int)-1;
> +}
> +
> +static void no_serial_out(struct uart_port *p, int offset, int value)
> +{
> +}
> +
> +#ifdef CONFIG_HAS_IOPORT
> +static inline bool is_upf_fourport(struct uart_port *port)
> +{
> +	return port->flags & UPF_FOURPORT;
> +}
> +#else
> +#define is_upf_fourport(x)	false
> +#endif
>  
>  static int serial8250_default_handle_irq(struct uart_port *port);
>  
> @@ -422,10 +443,12 @@ static void set_io_from_upio(struct uart_port *p)
>  	up->dl_write = default_serial_dl_write;
>  
>  	switch (p->iotype) {
> +#ifdef CONFIG_HAS_IOPORT
>  	case UPIO_HUB6:
>  		p->serial_in = hub6_serial_in;
>  		p->serial_out = hub6_serial_out;
>  		break;
> +#endif
>  
>  	case UPIO_MEM:
>  		p->serial_in = mem_serial_in;
> @@ -446,11 +469,16 @@ static void set_io_from_upio(struct uart_port *p)
>  		p->serial_in = mem32be_serial_in;
>  		p->serial_out = mem32be_serial_out;
>  		break;
> -
> -	default:
> +#ifdef CONFIG_HAS_IOPORT
> +	case UPIO_PORT:
>  		p->serial_in = io_serial_in;
>  		p->serial_out = io_serial_out;
>  		break;
> +#endif
> +	default:
> +		WARN(1, "Unsupported UART type %x\n", p->iotype);
> +		p->serial_in = no_serial_in;
> +		p->serial_out = no_serial_out;
>  	}
>  	/* Remember loaded iotype */
>  	up->cur_iotype = p->iotype;
> @@ -1318,7 +1346,7 @@ static void autoconfig_irq(struct uart_8250_port *up)
>  	unsigned long irqs;
>  	int irq;
>  
> -	if (port->flags & UPF_FOURPORT) {
> +	if (is_upf_fourport(port)) {
>  		ICP = (port->iobase & 0xfe0) | 0x1f;
>  		save_ICP = inb_p(ICP);
>  		outb_p(0x80, ICP);
> @@ -1337,7 +1365,7 @@ static void autoconfig_irq(struct uart_8250_port *up)
>  	irqs = probe_irq_on();
>  	serial8250_out_MCR(up, 0);
>  	udelay(10);
> -	if (port->flags & UPF_FOURPORT) {
> +	if (is_upf_fourport(port)) {
>  		serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS);
>  	} else {
>  		serial8250_out_MCR(up,
> @@ -1361,7 +1389,7 @@ static void autoconfig_irq(struct uart_8250_port *up)
>  	serial_out(up, UART_IER, save_ier);
>  	uart_port_unlock_irq(port);
>  
> -	if (port->flags & UPF_FOURPORT)
> +	if (is_upf_fourport(port))
>  		outb_p(save_ICP, ICP);
>  
>  	port->irq = (irq > 0) ? irq : 0;
> @@ -2438,7 +2466,7 @@ int serial8250_do_startup(struct uart_port *port)
>  	 */
>  	up->ier = UART_IER_RLSI | UART_IER_RDI;
>  
> -	if (port->flags & UPF_FOURPORT) {
> +	if (is_upf_fourport(port)) {
>  		unsigned int icp;
>  		/*
>  		 * Enable interrupts on the AST Fourport board
> @@ -2483,7 +2511,7 @@ void serial8250_do_shutdown(struct uart_port *port)
>  		serial8250_release_dma(up);
>  
>  	uart_port_lock_irqsave(port, &flags);
> -	if (port->flags & UPF_FOURPORT) {
> +	if (is_upf_fourport(port)) {
>  		/* reset interrupts on the AST Fourport board */
>  		inb((port->iobase & 0xfe0) | 0x1f);
>  		port->mctrl |= TIOCM_OUT1;
> diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
> index 47ff50763c04..54bf98869abf 100644
> --- a/drivers/tty/serial/8250/Kconfig
> +++ b/drivers/tty/serial/8250/Kconfig
> @@ -6,7 +6,6 @@
>  
>  config SERIAL_8250
>  	tristate "8250/16550 and compatible serial support"
> -	depends on !S390

Why? Your changelogs gives zero insight on this change.

>  	select SERIAL_CORE
>  	select SERIAL_MCTRL_GPIO if GPIOLIB
>  	help
> @@ -72,7 +71,7 @@ config SERIAL_8250_16550A_VARIANTS
>  
>  config SERIAL_8250_FINTEK
>  	bool "Support for Fintek variants"
> -	depends on SERIAL_8250
> +	depends on SERIAL_8250 && HAS_IOPORT
>  	help
>  	  Selecting this option will add support for the RS232 and RS485
>  	  capabilities of the Fintek F81216A LPC to 4 UART as well similar
> @@ -136,7 +135,7 @@ config SERIAL_8250_PCILIB
>  
>  config SERIAL_8250_PCI
>  	tristate "8250/16550 PCI device support"
> -	depends on SERIAL_8250 && PCI
> +	depends on SERIAL_8250 && PCI && HAS_IOPORT
>  	select SERIAL_8250_PCILIB
>  	default SERIAL_8250
>  	help
> @@ -163,7 +162,7 @@ config SERIAL_8250_HP300
>  
>  config SERIAL_8250_CS
>  	tristate "8250/16550 PCMCIA device support"
> -	depends on PCMCIA && SERIAL_8250
> +	depends on PCMCIA && SERIAL_8250 && HAS_IOPORT
>  	help
>  	  Say Y here to enable support for 16-bit PCMCIA serial devices,
>  	  including serial port cards, modems, and the modem functions of

What about drivers that use SERIAL8250_PORT()?

Also port provided in 8250_PNP might expect it I think.

> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index ffcf4882b25f..92c85e805c2a 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -874,7 +874,7 @@ config SERIAL_TXX9_STDSERIAL
>  
>  config SERIAL_JSM
>  	tristate "Digi International NEO and Classic PCI Support"
> -	depends on PCI
> +	depends on PCI && HAS_IOPORT
>  	select SERIAL_CORE
>  	help
>  	  This is a driver for Digi International's Neo and Classic series
>
Arnd Bergmann April 8, 2024, 10:17 a.m. UTC | #2
On Mon, Apr 8, 2024, at 11:54, Ilpo Järvinen wrote:
> On Fri, 5 Apr 2024, Niklas Schnelle wrote:

>>  config SERIAL_8250_CS
>>  	tristate "8250/16550 PCMCIA device support"
>> -	depends on PCMCIA && SERIAL_8250
>> +	depends on PCMCIA && SERIAL_8250 && HAS_IOPORT
>>  	help
>>  	  Say Y here to enable support for 16-bit PCMCIA serial devices,
>>  	  including serial port cards, modems, and the modem functions of
>
> What about drivers that use SERIAL8250_PORT()?

It probably makes sense to hide these, since they won't ever
work. I probably missed them in my initial series because they
don't cause a compile-time error, but I agree that there is no
use in showing the options here.

> Also port provided in 8250_PNP might expect it I think.

I don't think these need any change: 8250_pnp.c supports
both IORESOURCE_IO and IORESOURCE_MEM based ports. It will
still create a 8250 port for the I/O based ones but they
will now correctly fail to probe in the main driver rather
than crashing the kernel. PNP devices that only use
memory BARs will keep working as before, on both machines
with and without CONFIG_HAS_IOPORT.

I think that most 8250_pnp variants are probably used only
with ISAPNP or PNPBIOS, neither of which exists without
HAS_IOPORT, but you could certainly have PNPACPI on arm
or riscv machines that don't have port I/O but come with
a memory-mapped 8250 port described by firmware.

    Arnd
Ilpo Järvinen April 8, 2024, 10:25 a.m. UTC | #3
On Mon, 8 Apr 2024, Arnd Bergmann wrote:

> On Mon, Apr 8, 2024, at 11:54, Ilpo Järvinen wrote:
> > On Fri, 5 Apr 2024, Niklas Schnelle wrote:
> 
> >>  config SERIAL_8250_CS
> >>  	tristate "8250/16550 PCMCIA device support"
> >> -	depends on PCMCIA && SERIAL_8250
> >> +	depends on PCMCIA && SERIAL_8250 && HAS_IOPORT
> >>  	help
> >>  	  Say Y here to enable support for 16-bit PCMCIA serial devices,
> >>  	  including serial port cards, modems, and the modem functions of
> >
> > What about drivers that use SERIAL8250_PORT()?
> 
> It probably makes sense to hide these, since they won't ever
> work. I probably missed them in my initial series because they
> don't cause a compile-time error, but I agree that there is no
> use in showing the options here.
> 
> > Also port provided in 8250_PNP might expect it I think.
> 
> I don't think these need any change: 8250_pnp.c supports
> both IORESOURCE_IO and IORESOURCE_MEM based ports. It will
> still create a 8250 port for the I/O based ones but they
> will now correctly fail to probe in the main driver rather
> than crashing the kernel. PNP devices that only use
> memory BARs will keep working as before, on both machines
> with and without CONFIG_HAS_IOPORT.
> 
> I think that most 8250_pnp variants are probably used only
> with ISAPNP or PNPBIOS, neither of which exists without
> HAS_IOPORT,

Okay, seems fine then if that dependency is handled somewhere.
Niklas Schnelle April 8, 2024, 3:35 p.m. UTC | #4
On Mon, 2024-04-08 at 12:54 +0300, Ilpo Järvinen wrote:
> On Fri, 5 Apr 2024, Niklas Schnelle wrote:
> 
> > In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
> > compile time. We thus need to add HAS_IOPORT as dependency for those
> > drivers using them unconditionally. For 8250 based drivers some support
> > MMIO only use so fence only the parts requiring I/O ports.
> > 
> > Co-developed-by: Arnd Bergmann <arnd@kernel.org>
> > Signed-off-by: Arnd Bergmann <arnd@kernel.org>
> > Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> > ---
> > Note: This patch does not depend any not-yet-mainline HAS_IOPORT changes
> > and may be merged via subsystem specific trees at your earliest
> > convenience.
> > 
> > Note 2: This was previously acked here:
> > https://lore.kernel.org/all/2023053050-prodigal-shine-4d1c@gregkh/
> > Given this was almost a year ago and didn't apply then I didn't
> > carry the Ack over though.
> > 
> > 
---8<---
> > diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
> > index 47ff50763c04..54bf98869abf 100644
> > --- a/drivers/tty/serial/8250/Kconfig
> > +++ b/drivers/tty/serial/8250/Kconfig
> > @@ -6,7 +6,6 @@
> >  
> >  config SERIAL_8250
> >  	tristate "8250/16550 and compatible serial support"
> > -	depends on !S390
> 
> Why? Your changelogs gives zero insight on this change.

I used this for compile testing since I build on s390 natively and this
would have hidden the missing HAS_IOPORT dependencies I'm pretty sure
it was added because of the I/O port problem too. I'll either add to
the commit description that it is no longer needed or drop this. Any
preference?
Ilpo Järvinen April 8, 2024, 3:41 p.m. UTC | #5
On Mon, 8 Apr 2024, Niklas Schnelle wrote:

> On Mon, 2024-04-08 at 12:54 +0300, Ilpo Järvinen wrote:
> > On Fri, 5 Apr 2024, Niklas Schnelle wrote:
> > 
> > > In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
> > > compile time. We thus need to add HAS_IOPORT as dependency for those
> > > drivers using them unconditionally. For 8250 based drivers some support
> > > MMIO only use so fence only the parts requiring I/O ports.
> > > 
> > > Co-developed-by: Arnd Bergmann <arnd@kernel.org>
> > > Signed-off-by: Arnd Bergmann <arnd@kernel.org>
> > > Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> > > ---
> > > Note: This patch does not depend any not-yet-mainline HAS_IOPORT changes
> > > and may be merged via subsystem specific trees at your earliest
> > > convenience.
> > > 
> > > Note 2: This was previously acked here:
> > > https://lore.kernel.org/all/2023053050-prodigal-shine-4d1c@gregkh/
> > > Given this was almost a year ago and didn't apply then I didn't
> > > carry the Ack over though.
> > > 
> > > 
> ---8<---
> > > diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
> > > index 47ff50763c04..54bf98869abf 100644
> > > --- a/drivers/tty/serial/8250/Kconfig
> > > +++ b/drivers/tty/serial/8250/Kconfig
> > > @@ -6,7 +6,6 @@
> > >  
> > >  config SERIAL_8250
> > >  	tristate "8250/16550 and compatible serial support"
> > > -	depends on !S390
> > 
> > Why? Your changelogs gives zero insight on this change.
> 
> I used this for compile testing since I build on s390 natively and this
> would have hidden the missing HAS_IOPORT dependencies I'm pretty sure
> it was added because of the I/O port problem too. I'll either add to
> the commit description that it is no longer needed or drop this. Any
> preference?

Okay, we might never know the reason for sure if that's old enough.
I think the best approach would be to put it into own patch so this 
guessimation is limited to a single liner patch instead of it being 
hidden among the other clearer cases.
Arnd Bergmann April 8, 2024, 3:50 p.m. UTC | #6
On Mon, Apr 8, 2024, at 17:41, Ilpo Järvinen wrote:
> On Mon, 8 Apr 2024, Niklas Schnelle wrote:
>> On Mon, 2024-04-08 at 12:54 +0300, Ilpo Järvinen wrote:
>> > On Fri, 5 Apr 2024, Niklas Schnelle wrote:
>> ---8<---
>> > > diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
>> > > index 47ff50763c04..54bf98869abf 100644
>> > > --- a/drivers/tty/serial/8250/Kconfig
>> > > +++ b/drivers/tty/serial/8250/Kconfig
>> > > @@ -6,7 +6,6 @@
>> > >  
>> > >  config SERIAL_8250
>> > >  	tristate "8250/16550 and compatible serial support"
>> > > -	depends on !S390
>> > 
>> > Why? Your changelogs gives zero insight on this change.
>> 
>> I used this for compile testing since I build on s390 natively and this
>> would have hidden the missing HAS_IOPORT dependencies I'm pretty sure
>> it was added because of the I/O port problem too. I'll either add to
>> the commit description that it is no longer needed or drop this. Any
>> preference?
>
> Okay, we might never know the reason for sure if that's old enough.
> I think the best approach would be to put it into own patch so this 
> guessimation is limited to a single liner patch instead of it being 
> hidden among the other clearer cases.
Maciej W. Rozycki May 23, 2024, 2:11 a.m. UTC | #7
On Fri, 5 Apr 2024, Niklas Schnelle wrote:

> diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
> index 0d35c77fad9e..38ac5236d2ea 100644
> --- a/drivers/tty/serial/8250/8250_pci.c
> +++ b/drivers/tty/serial/8250/8250_pci.c
> @@ -928,6 +928,7 @@ static int pci_netmos_init(struct pci_dev *dev)
>  	return num_serial;
>  }
>  
> +#ifdef CONFIG_HAS_IOPORT
>  /*
>   * These chips are available with optionally one parallel port and up to
>   * two serial ports. Unfortunately they all have the same product id.
[...]
> diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
> index 47ff50763c04..54bf98869abf 100644
> --- a/drivers/tty/serial/8250/Kconfig
> +++ b/drivers/tty/serial/8250/Kconfig
> @@ -136,7 +135,7 @@ config SERIAL_8250_PCILIB
>  
>  config SERIAL_8250_PCI
>  	tristate "8250/16550 PCI device support"
> -	depends on SERIAL_8250 && PCI
> +	depends on SERIAL_8250 && PCI && HAS_IOPORT
>  	select SERIAL_8250_PCILIB
>  	default SERIAL_8250
>  	help

 This is clearly wrong, there is PCIe 8250 serial hardware that does MMIO 
only, so the option has to stay possible to enable.  I have such hardware 
as shown in this log:

Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
serial 0001:01:00.0: enabling device (0140 -> 0142)
serial 0001:01:00.0: detected caps 00000700 should be 00000500
0001:01:00.0: ttyS0 at MMIO 0x600c080401000 (irq = 40, base_baud = 15625000) is a 16C950/954
serial 0001:01:00.0: detected caps 00000700 should be 00000500
0001:01:00.0: ttyS1 at MMIO 0x600c080401200 (irq = 40, base_baud = 15625000) is a 16C950/954

which is from a POWER9 system.  Which as you may know has no PCI port I/O 
support in hardware, so it is quite relevant here.  I'd like to keep this 
PCIe serial option functional with my system.

 Also your change itself modifies 8250_pci.c (cited above for reference), 
which would make no sense if SERIAL_8250_PCI was permanently disabled for 
!HAS_IOPORT.  Shall I take it than that the Kconfig change I question has 
been made merely by mistake?

  Maciej
Niklas Schnelle Oct. 1, 2024, 9:04 a.m. UTC | #8
On Mon, 2024-04-08 at 12:17 +0200, Arnd Bergmann wrote:
> On Mon, Apr 8, 2024, at 11:54, Ilpo Järvinen wrote:
> > On Fri, 5 Apr 2024, Niklas Schnelle wrote:
> 
> > >  config SERIAL_8250_CS
> > >  	tristate "8250/16550 PCMCIA device support"
> > > -	depends on PCMCIA && SERIAL_8250
> > > +	depends on PCMCIA && SERIAL_8250 && HAS_IOPORT
> > >  	help
> > >  	  Say Y here to enable support for 16-bit PCMCIA serial devices,
> > >  	  including serial port cards, modems, and the modem functions of
> > 
> > What about drivers that use SERIAL8250_PORT()?
> 
> It probably makes sense to hide these, since they won't ever
> work. I probably missed them in my initial series because they
> don't cause a compile-time error, but I agree that there is no
> use in showing the options here.
> 

As far as I can tell SERTIAL8250_PORT() is used by SERIAL_8250_ACCENT,
SERIAL_8250_BOCA, and SERIAL_8250_EXAR_ST16C554 all of these already
depend on ISA which implies HAS_IOPORT. So I don't think we need to add
HAS_IOPORT dependencies here?

Thanks,
Niklas
Niklas Schnelle Oct. 1, 2024, 11:21 a.m. UTC | #9
On Thu, 2024-05-23 at 03:11 +0100, Maciej W. Rozycki wrote:
> On Fri, 5 Apr 2024, Niklas Schnelle wrote:
> 
> > diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
> > index 0d35c77fad9e..38ac5236d2ea 100644
> > --- a/drivers/tty/serial/8250/8250_pci.c
> > +++ b/drivers/tty/serial/8250/8250_pci.c
> > @@ -928,6 +928,7 @@ static int pci_netmos_init(struct pci_dev *dev)
> >  	return num_serial;
> >  }
> >  
> > +#ifdef CONFIG_HAS_IOPORT
> >  /*
> >   * These chips are available with optionally one parallel port and up to
> >   * two serial ports. Unfortunately they all have the same product id.
> [...]
> > diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
> > index 47ff50763c04..54bf98869abf 100644
> > --- a/drivers/tty/serial/8250/Kconfig
> > +++ b/drivers/tty/serial/8250/Kconfig
> > @@ -136,7 +135,7 @@ config SERIAL_8250_PCILIB
> >  
> >  config SERIAL_8250_PCI
> >  	tristate "8250/16550 PCI device support"
> > -	depends on SERIAL_8250 && PCI
> > +	depends on SERIAL_8250 && PCI && HAS_IOPORT
> >  	select SERIAL_8250_PCILIB
> >  	default SERIAL_8250
> >  	help
> 
>  This is clearly wrong, there is PCIe 8250 serial hardware that does MMIO 
> only, so the option has to stay possible to enable.  I have such hardware 
> as shown in this log:
> 
> Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
> serial 0001:01:00.0: enabling device (0140 -> 0142)
> serial 0001:01:00.0: detected caps 00000700 should be 00000500
> 0001:01:00.0: ttyS0 at MMIO 0x600c080401000 (irq = 40, base_baud = 15625000) is a 16C950/954
> serial 0001:01:00.0: detected caps 00000700 should be 00000500
> 0001:01:00.0: ttyS1 at MMIO 0x600c080401200 (irq = 40, base_baud = 15625000) is a 16C950/954
> 
> which is from a POWER9 system.  Which as you may know has no PCI port I/O 
> support in hardware, so it is quite relevant here.  I'd like to keep this 
> PCIe serial option functional with my system.
> 
>  Also your change itself modifies 8250_pci.c (cited above for reference), 
> which would make no sense if SERIAL_8250_PCI was permanently disabled for 
> !HAS_IOPORT.  Shall I take it than that the Kconfig change I question has 
> been made merely by mistake?
> 
>   Maciej

Hi Maciej,

With 2 more HAS_IOPORT patches having gone into v6.12-rc1 I'm looking
at what's left and we're down to 4 prerequisite patches[0] before being
able to compile-time disable inb()/outb()/…. This one being by far the
largest of these. Looking at your suggestion it seems that to compile
8250_pci.c without HAS_IOPORT I'll have to add #ifdef CONFIG_HAS_IOPORT
around the MOXI section as that uses I/O ports unconditionally. The
rest seems fine and I guess would theoretically work on a system with
!HAS_IOPORT. I'll send a v2 with that included. 

Note however that even though your POWER9 system does not have I/O port
support in hardware we still have HAS_IOPORT enabled for arch/powerpc
if PCI is enabed so even with this patch as is your POWER9 system
should not be affected.

Thanks,
Niklas

[0]
https://git.kernel.org/pub/scm/linux/kernel/git/niks/linux.git/log/?h=has_ioport
Arnd Bergmann Oct. 1, 2024, 3:31 p.m. UTC | #10
On Tue, Oct 1, 2024, at 11:21, Niklas Schnelle wrote:
> On Thu, 2024-05-23 at 03:11 +0100, Maciej W. Rozycki wrote:
>
> With 2 more HAS_IOPORT patches having gone into v6.12-rc1 I'm looking
> at what's left and we're down to 4 prerequisite patches[0] before being
> able to compile-time disable inb()/outb()/…. This one being by far the
> largest of these. Looking at your suggestion it seems that to compile
> 8250_pci.c without HAS_IOPORT I'll have to add #ifdef CONFIG_HAS_IOPORT
> around the MOXI section as that uses I/O ports unconditionally. The
> rest seems fine and I guess would theoretically work on a system with
> !HAS_IOPORT. I'll send a v2 with that included. 

I think that is the correct approach, yes. From what I can tell, the
older version of the 8250 patch added the #ifdef blocks for all other
port types that need port I/O, but the moxa version was added later
than that and just needs the same change.

      Arnd
Maciej W. Rozycki Oct. 1, 2024, 4:41 p.m. UTC | #11
Hi Niklas,

> With 2 more HAS_IOPORT patches having gone into v6.12-rc1 I'm looking
> at what's left and we're down to 4 prerequisite patches[0] before being
> able to compile-time disable inb()/outb()/…. This one being by far the
> largest of these. Looking at your suggestion it seems that to compile
> 8250_pci.c without HAS_IOPORT I'll have to add #ifdef CONFIG_HAS_IOPORT
> around the MOXI section as that uses I/O ports unconditionally. The
> rest seems fine and I guess would theoretically work on a system with
> !HAS_IOPORT. I'll send a v2 with that included. 

 I've skimmed over and I agree, though I'd place some of the #ifdefs 
differently, e.g. above #define QPCR_TEST_FOR1.  Overall I think it would 
make sense to reorder code and group stuff that depends on port I/O such 
as to minimise the number of #ifdefs.

 Ideally we could come with a slightly user-friendlier change that would 
report the inability to handle port I/O devices as they are discovered 
rather than just silently ignoring them.

> Note however that even though your POWER9 system does not have I/O port
> support in hardware we still have HAS_IOPORT enabled for arch/powerpc
> if PCI is enabed so even with this patch as is your POWER9 system
> should not be affected.

 I think we need to get this right regardless.  And also while I run a 
generic distribution kernel on my POWER9 as a production system, I'd love 
to see an option to build a tailored configuration that would indeed 
remove support for port I/O from the kernel side for systems like mine 
that have no provision for port I/O in hardware, knowing that such a 
kernel will only ever run on such hardware and discarding compiled code 
that won't ever be used.

  Maciej
Niklas Schnelle Oct. 2, 2024, 12:44 p.m. UTC | #12
On Tue, 2024-10-01 at 17:41 +0100, Maciej W. Rozycki wrote:
> Hi Niklas,
> 
> > With 2 more HAS_IOPORT patches having gone into v6.12-rc1 I'm looking
> > at what's left and we're down to 4 prerequisite patches[0] before being
> > able to compile-time disable inb()/outb()/…. This one being by far the
> > largest of these. Looking at your suggestion it seems that to compile
> > 8250_pci.c without HAS_IOPORT I'll have to add #ifdef CONFIG_HAS_IOPORT
> > around the MOXI section as that uses I/O ports unconditionally. The
> > rest seems fine and I guess would theoretically work on a system with
> > !HAS_IOPORT. I'll send a v2 with that included. 
> 
>  I've skimmed over and I agree, though I'd place some of the #ifdefs 
> differently, e.g. above #define QPCR_TEST_FOR1.  Overall I think it would 
> make sense to reorder code and group stuff that depends on port I/O such 
> as to minimise the number of #ifdefs.

I just noticed that while not causing compile errors pci_fintek_setup()
also sets iotype = UPIO_PORT so the devices using this won't work
without HAS_IOPORT either. 

> 
>  Ideally we could come with a slightly user-friendlier change that would 
> report the inability to handle port I/O devices as they are discovered 
> rather than just silently ignoring them.

I think this would generally get quite ugly as one would have to keep
around enough of the drivers which can't possibly work in that
!HAS_IOPORT kernel to identify the device and print some error. It's
also not what happens when anything else isn't supported by your kernel
build. And I don't think we can just look for any I/O ports either
because they could be an alternative access method that isn't required.

As an example for the ugliness, for 8250 one could get something to
work as it supports both I/O port and MMIO devices. First one would
need to not #ifdef the setup routines and keep the quirk entry for
devices that use UPIO_PORT and then in pciserial_init_ports() one could
check with !IS_ENABLED(CONFIG_HAS_IOPORT) && uart.port.iotype ==
UPIO_PORT. But then for moxa one would have to keep pci_moxa_setup() to
set iotype = UPIO_PORT but would have to #ifdef in pci_moxa_init()
because the initialization already uses I/O ports and init is part of
the quirk.

> 
> > Note however that even though your POWER9 system does not have I/O port
> > support in hardware we still have HAS_IOPORT enabled for arch/powerpc
> > if PCI is enabed so even with this patch as is your POWER9 system
> > should not be affected.
> 
>  I think we need to get this right regardless.  And also while I run a 
> generic distribution kernel on my POWER9 as a production system, I'd love 
> to see an option to build a tailored configuration that would indeed 
> remove support for port I/O from the kernel side for systems like mine 
> that have no provision for port I/O in hardware, knowing that such a 
> kernel will only ever run on such hardware and discarding compiled code 
> that won't ever be used.
> 
>   Maciej

I think allowing for such custom configs is a possible second step and
I agree it would be nice and probably becomes more useful as more and
more platforms lose I/O port support. First we need to be able to build
without HAS_IOPORT on architectures that just can't do I/O port access
though, and I'd like not delay this any more.

Thanks,
Niklas
Maciej W. Rozycki Oct. 2, 2024, 6:12 p.m. UTC | #13
On Wed, 2 Oct 2024, Niklas Schnelle wrote:

> >  Ideally we could come with a slightly user-friendlier change that would 
> > report the inability to handle port I/O devices as they are discovered 
> > rather than just silently ignoring them.
> 
> I think this would generally get quite ugly as one would have to keep
> around enough of the drivers which can't possibly work in that
> !HAS_IOPORT kernel to identify the device and print some error. It's
> also not what happens when anything else isn't supported by your kernel
> build. And I don't think we can just look for any I/O ports either
> because they could be an alternative access method that isn't required.

 There might be corner cases, but offhand I think it's simpler than you 
outline.  There are two cases to handle here:

1. Code you've #ifdef'd out that explicitly refers port I/O resources.
   So rather than having struct entries referring to problematic `*_init' 
   handlers #ifdef'd out we can keep them and make them call an error 
   reporting function if (!IS_ENABLED(CONFIG_HAS_IOPORT)).  As a side 
   effect the structure of code will improve as we don't really like 
   #ifdefs sprinkled throughout.

2. Code that infers the access type required from BARs.  It has to handle 
   the unsupported case anyway, so rather than doing it silently it can 
   call the same error reporting function.

Yes, there's some work to be done here, but nothing exceedingly tough I 
believe.

 Also I think this case is a bit special, because it's different from a 
missing driver.  The driver is there and the hardware is there visible in 
the PCI hierarchy, there's nothing reported and other serial ports work, 
or a similar serial port works elsewhere, so why doesn't this one?  The 
user may not necessarily be aware of the peculiarity that the lack of 
support for port I/O is.

 I was not and discovered it the hard way in the course of installing my 
POWER9 system and trying to make the defxx driver work as supplied by the 
distribution.  It took me a few days to conclude there is no bug anywhere 
except for the system lacking support for port I/O and the driver having 
been configured by the packager via a Kconfig option to use that access 
type.  Also I had PHB4 documentation to hand to refer to and track down 
the relevant bits.

 I ended up updating the driver to choose the access type automatically 
(as the board resources are dual-mapped, via both a port I/O and an MMIO 
BAR), and would have done so long before if I was aware of the existence 
of such systems.

 Now I consider myself a reasonably seasoned systems software developer, 
so what can an ordinary user say?  They might be utterly confused and 
either report it as a system bug (if they were so determined) or just 
conclude Linux is junk.

 A message such as:

serial 0001:01:00.0: cannot handle, no port I/O support in the system

would definitely help.

> As an example for the ugliness, for 8250 one could get something to
> work as it supports both I/O port and MMIO devices. First one would
> need to not #ifdef the setup routines and keep the quirk entry for
> devices that use UPIO_PORT and then in pciserial_init_ports() one could
> check with !IS_ENABLED(CONFIG_HAS_IOPORT) && uart.port.iotype ==
> UPIO_PORT. But then for moxa one would have to keep pci_moxa_setup() to
> set iotype = UPIO_PORT but would have to #ifdef in pci_moxa_init()
> because the initialization already uses I/O ports and init is part of
> the quirk.

 I don't think it has to be as complex as that.  The OxSemi Tornado driver 
which I care about a lot is an example of one that handles hardware that 
can be strapped for either access type (and I have cards with actual pin 
header jumpers and associated documentation for the user to configure 
that), so you only know at run time from the type of BAR 0 whether you 
need port I/O or MMIO.  So it falls into #2 above, and all you need is to 
handle this in `serial8250_pci_setup_port', which I can see your change 
doesn't take into account anyway, whether silently or aloud.

> I think allowing for such custom configs is a possible second step and
> I agree it would be nice and probably becomes more useful as more and
> more platforms lose I/O port support. First we need to be able to build
> without HAS_IOPORT on architectures that just can't do I/O port access
> though, and I'd like not delay this any more.

 I agree it will best be done in steps, no worry.

  Maciej
Arnd Bergmann Oct. 2, 2024, 10 p.m. UTC | #14
On Wed, Oct 2, 2024, at 18:12, Maciej W. Rozycki wrote:
> On Wed, 2 Oct 2024, Niklas Schnelle wrote:
>
>> >  Ideally we could come with a slightly user-friendlier change that would 
>> > report the inability to handle port I/O devices as they are discovered 
>> > rather than just silently ignoring them.
>> 
>> I think this would generally get quite ugly as one would have to keep
>> around enough of the drivers which can't possibly work in that
>> !HAS_IOPORT kernel to identify the device and print some error. It's
>> also not what happens when anything else isn't supported by your kernel
>> build. And I don't think we can just look for any I/O ports either
>> because they could be an alternative access method that isn't required.
>
>  There might be corner cases, but offhand I think it's simpler than you 
> outline.  There are two cases to handle here:
>
> 1. Code you've #ifdef'd out that explicitly refers port I/O resources.
>    So rather than having struct entries referring to problematic `*_init' 
>    handlers #ifdef'd out we can keep them and make them call an error 
>    reporting function if (!IS_ENABLED(CONFIG_HAS_IOPORT)).  As a side 
>    effect the structure of code will improve as we don't really like 
>    #ifdefs sprinkled throughout.
>
> 2. Code that infers the access type required from BARs.  It has to handle 
>    the unsupported case anyway, so rather than doing it silently it can 
>    call the same error reporting function.
>
> Yes, there's some work to be done here, but nothing exceedingly tough I 
> believe.

I agree that this shouldn't be hard to finish. The IS_ENABLED()
check is not that easy to do as I think we need to keep calling
inb()/outb() outside of an #ifdef a compile-time error.

However, I think most of the inb/outb usage in 8250_pci.c can
just be converted to either serial_port_in()/serial_port_out(),
using the 8250 specific wrappers, or to ioread8()/iowrite8()
in combination with pci_iomap().

It might help to add a UPIO_IOMAP type to replace UPIO_PORT
for the PCI drivers and just use pci_iomap() exclusively in that
driver.

>  Also I think this case is a bit special, because it's different from a 
> missing driver.  The driver is there and the hardware is there visible in 
> the PCI hierarchy, there's nothing reported and other serial ports work, 
> or a similar serial port works elsewhere, so why doesn't this one?  The 
> user may not necessarily be aware of the peculiarity that the lack of 
> support for port I/O is.

Part of the problem that Niklas is trying to solve with the
CONFIG_HAS_IOPORT annotations is to prevent an invalid inb()/outb()
from turning into a NULL pointer dereference as it currently does
on architectures that have no way to support PIO but get the
default implementation from asm-generic/io.h.

It's not clear if having a silently non-working driver or one
that crashes makes it easier to debug for users. Having a clear
warning message in the PCI probe code is probably the best
we can hope for.

>  I was not and discovered it the hard way in the course of installing my 
> POWER9 system and trying to make the defxx driver work as supplied by the 
> distribution.  It took me a few days to conclude there is no bug anywhere 
> except for the system lacking support for port I/O and the driver having 
> been configured by the packager via a Kconfig option to use that access 
> type.  Also I had PHB4 documentation to hand to refer to and track down 
> the relevant bits.
>
>  I ended up updating the driver to choose the access type automatically 
> (as the board resources are dual-mapped, via both a port I/O and an MMIO 
> BAR), and would have done so long before if I was aware of the existence 
> of such systems.
>
>  Now I consider myself a reasonably seasoned systems software developer, 
> so what can an ordinary user say?  They might be utterly confused and 
> either report it as a system bug (if they were so determined) or just 
> conclude Linux is junk.

I think that anyone using hardware that relies on port I/O on
non-x86 is at this point going to have a reasonable understanding
of the system, so I'm not too worried here. ;-)

>  A message such as:
>
> serial 0001:01:00.0: cannot handle, no port I/O support in the system
>
> would definitely help.

Right.

       Arnd
Maciej W. Rozycki Oct. 2, 2024, 10:59 p.m. UTC | #15
On Wed, 2 Oct 2024, Arnd Bergmann wrote:

> I agree that this shouldn't be hard to finish. The IS_ENABLED()
> check is not that easy to do as I think we need to keep calling
> inb()/outb() outside of an #ifdef a compile-time error.

 Can we just provide dummy prototypes with `__attribute__((error("...")))' 
instead?  This will surely prevent us from having to bend backwards so as 
to make sure the compiler won't see any spurious references to these 
inexistent functions or macros.  We already have a `__compiletime_error()' 
macro for this purpose even.

> Part of the problem that Niklas is trying to solve with the
> CONFIG_HAS_IOPORT annotations is to prevent an invalid inb()/outb()
> from turning into a NULL pointer dereference as it currently does
> on architectures that have no way to support PIO but get the
> default implementation from asm-generic/io.h.

 It can be worse than that.  Part of my confusion with the defxx driver 
trying to do port I/O with my POWER9 system came from the mapping actually 
resulting in non-NULL invalid pointers, dereferencing which caused a flood 
of obscure messages produced to the system console by the system firmware:

LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
IPMI: dropping non severe PEL event
LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
IPMI: dropping non severe PEL event
LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
IPMI: dropping non severe PEL event
LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
IPMI: dropping non severe PEL event
[...]

from which it was all but obvious that they were caused by an attempt to 
use PCI port I/O with a system lacking support for it.

> It's not clear if having a silently non-working driver or one
> that crashes makes it easier to debug for users. Having a clear
> warning message in the PCI probe code is probably the best
> we can hope for.

 I agree.  Enthusiastically.

> I think that anyone using hardware that relies on port I/O on
> non-x86 is at this point going to have a reasonable understanding
> of the system, so I'm not too worried here. ;-)

 Well, virtually all non-x86 systems continue supporting PCI/e port I/O 
via a suitably decoded CPU-side MMIO window, so I think coming across one 
that doesn't can still be a nasty surprise even in 2024.  For instance 
I've been happily using a PC parallel port PCIe option card, one of the 
very few interfaces if not the only one remaining that have not ever seen 
an MMIO variant, with my RISC-V hardware, newer than said POWER9 system.

 So far it's been the s390 and a couple of POWER system implementations 
that have support for PCI/e port I/O removed.  Have I missed anything?

  Maciej
Arnd Bergmann Oct. 4, 2024, 6:53 a.m. UTC | #16
On Wed, Oct 2, 2024, at 22:59, Maciej W. Rozycki wrote:
> On Wed, 2 Oct 2024, Arnd Bergmann wrote:
>> Part of the problem that Niklas is trying to solve with the
>> CONFIG_HAS_IOPORT annotations is to prevent an invalid inb()/outb()
>> from turning into a NULL pointer dereference as it currently does
>> on architectures that have no way to support PIO but get the
>> default implementation from asm-generic/io.h.
>
>  It can be worse than that.  Part of my confusion with the defxx driver 
> trying to do port I/O with my POWER9 system came from the mapping actually 
> resulting in non-NULL invalid pointers, dereferencing which caused a flood 
> of obscure messages produced to the system console by the system firmware:
>
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> [...]
>
> from which it was all but obvious that they were caused by an attempt to 
> use PCI port I/O with a system lacking support for it.

Ah, that's too bad. I think this is a result of the patch that
Michael did to shut up the NULL pointer warning we get, see
be140f1732b5 ("powerpc/64: Set _IO_BASE to POISON_POINTER_DELTA
not 0 for CONFIG_PCI=n"). I really wish we could have finished
Niklas' series earlier to avoid this.

>> I think that anyone using hardware that relies on port I/O on
>> non-x86 is at this point going to have a reasonable understanding
>> of the system, so I'm not too worried here. ;-)
>
>  Well, virtually all non-x86 systems continue supporting PCI/e port I/O 
> via a suitably decoded CPU-side MMIO window, so I think coming across one 
> that doesn't can still be a nasty surprise even in 2024.  For instance 
> I've been happily using a PC parallel port PCIe option card, one of the 
> very few interfaces if not the only one remaining that have not ever seen 
> an MMIO variant, with my RISC-V hardware, newer than said POWER9 system.
>
>  So far it's been the s390 and a couple of POWER system implementations 
> that have support for PCI/e port I/O removed.  Have I missed anything?

I meant PCIe cards with I/O space here, not host bridges. I know you
have a lot of them, but what I've heard from Arm platform maintainers
is that they tend to struggle finding any PCIe cards to test their
hsot bridge drivers on, and I expect that a lot of them are actually
broken because they have never been tested and just copied the
implementation badly from some other driver.

I think the only new PCIe devices you can find today that still use
I/O space are ones with compatibility registers for IBM PC style
hardware (vga, uart, parport), but most users would never have used
one of those and instead use the native register interface of their
GPU (on non-x86), USB-serial and no parport. Other devices that
needed I/O space never worked on PCIe anyway because of the lack
of ISA style DMA.

There are also a lot of Arm systems that have no I/O space support at
all, such as the Apple M2 I'm using at the moment.

      Arnd
Niklas Schnelle Oct. 4, 2024, 10:09 a.m. UTC | #17
On Wed, 2024-10-02 at 23:59 +0100, Maciej W. Rozycki wrote:
> On Wed, 2 Oct 2024, Arnd Bergmann wrote:
> 
> > I agree that this shouldn't be hard to finish. The IS_ENABLED()
> > check is not that easy to do as I think we need to keep calling
> > inb()/outb() outside of an #ifdef a compile-time error.
> 
>  Can we just provide dummy prototypes with `__attribute__((error("...")))' 
> instead?  This will surely prevent us from having to bend backwards so as 
> to make sure the compiler won't see any spurious references to these 
> inexistent functions or macros.  We already have a `__compiletime_error()' 
> macro for this purpose even.

This is already done in the final patch of my series when disabling
inb()/outb() and friends.


> 
> > Part of the problem that Niklas is trying to solve with the
> > CONFIG_HAS_IOPORT annotations is to prevent an invalid inb()/outb()
> > from turning into a NULL pointer dereference as it currently does
> > on architectures that have no way to support PIO but get the
> > default implementation from asm-generic/io.h.
> 
>  It can be worse than that.  Part of my confusion with the defxx driver 
> trying to do port I/O with my POWER9 system came from the mapping actually 
> resulting in non-NULL invalid pointers, dereferencing which caused a flood 
> of obscure messages produced to the system console by the system firmware:
> 
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> IPMI: dropping non severe PEL event
> [...]
> 
> from which it was all but obvious that they were caused by an attempt to 
> use PCI port I/O with a system lacking support for it.
> 
> > It's not clear if having a silently non-working driver or one
> > that crashes makes it easier to debug for users. Having a clear
> > warning message in the PCI probe code is probably the best
> > we can hope for.
> 
>  I agree.  Enthusiastically.

I think there was also a bit of a misunderstanding. My argument that
this would be very ugly in the general case was really meant as general
case outside of drivers like 8250 that deals with both I/O port and
MMIO i.e. we can't warn/error when !HAS_IOPORT deactivates a whole
driver because seeing an I/O port BAR in common PCI code doesn't mean
that it is required for use of the device.

I'm working on a new proposal for 8250 now. Basically I think we can
put the warning/error in serial8250_pci_setup_port(). And then for
those 8250_pci.c "sub drivers" that require I/O ports instead of just
ifdeffing out their setup/init/exit we can define anything but setup to
NULL and setup to pci_default_setup() such that the latter will find
the I/O port BAR via FL_GET_BASE() and subsequently cause the error
print in serial8250_pci_setup_port(). It's admittedly a bit odd but it
also keeps the #ifdefs to just around the code that wouldn't compile.

One thing I'm not happy with yet is the handling around
is_upf_fourport(port) in 8250_pci.c. With !HAS_IOPORT this is defined
as false. With that it makes sure that inb_p()/outb_p() aren't called
but I think this only works because the compiler usually drops the dead
if clause. I think there were previous discussions around this but I
think just like IS_ENABLED() checks this isn't quite correct.

Thanks,
Niklas
Arnd Bergmann Oct. 4, 2024, 12:48 p.m. UTC | #18
On Fri, Oct 4, 2024, at 10:09, Niklas Schnelle wrote:

> I'm working on a new proposal for 8250 now. Basically I think we can
> put the warning/error in serial8250_pci_setup_port(). And then for
> those 8250_pci.c "sub drivers" that require I/O ports instead of just
> ifdeffing out their setup/init/exit we can define anything but setup to
> NULL and setup to pci_default_setup() such that the latter will find
> the I/O port BAR via FL_GET_BASE() and subsequently cause the error
> print in serial8250_pci_setup_port(). It's admittedly a bit odd but it
> also keeps the #ifdefs to just around the code that wouldn't compile.

Right, makes sense.

> One thing I'm not happy with yet is the handling around
> is_upf_fourport(port) in 8250_pci.c. With !HAS_IOPORT this is defined
> as false. With that it makes sure that inb_p()/outb_p() aren't called
> but I think this only works because the compiler usually drops the dead
> if clause. I think there were previous discussions around this but I
> think just like IS_ENABLED() checks this isn't quite correct.

Not sure what you mean, we rely on dead code elimination in all
kinds of places. The only bit to be aware of is that normal
'inline' functions may not get constant-folded all the time,
but anything that is either a macro or __always_inline does
work.

I just verified that the version below does what Maciej
suggested with IS_ENABLED() in 8250-pci, and this works fine.

       Arnd

diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 6709b6a5f301..784190824aad 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -928,6 +928,14 @@ static int pci_netmos_init(struct pci_dev *dev)
 	return num_serial;
 }
 
+static int serial_8250_need_ioport(struct pci_dev *dev)
+{
+	dev_warn(&dev->dev,
+		 "Serial port not supported because of missing I/O resource\n");
+
+	return -ENXIO;
+}
+
 /*
  * These chips are available with optionally one parallel port and up to
  * two serial ports. Unfortunately they all have the same product id.
@@ -964,6 +972,9 @@ static int pci_ite887x_init(struct pci_dev *dev)
 	struct resource *iobase = NULL;
 	u32 miscr, uartbar, ioport;
 
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return serial_8250_need_ioport(dev);
+
 	/* search for the base-ioport */
 	for (i = 0; i < ARRAY_SIZE(inta_addr); i++) {
 		iobase = request_region(inta_addr[i], ITE_887x_IOSIZE,
@@ -1049,6 +1060,10 @@ static int pci_ite887x_init(struct pci_dev *dev)
 static void pci_ite887x_exit(struct pci_dev *dev)
 {
 	u32 ioport;
+
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		serial_8250_need_ioport(dev);
+
 	/* the ioport is bit 0-15 in POSIO0R */
 	pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport);
 	ioport &= 0xffff;
@@ -1514,6 +1529,9 @@ static int pci_quatech_init(struct pci_dev *dev)
 	const struct pci_device_id *match;
 	bool amcc = false;
 
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return serial_8250_need_ioport(dev);
+
 	match = pci_match_id(quatech_cards, dev);
 	if (match)
 		amcc = match->driver_data;
@@ -1538,6 +1556,9 @@ static int pci_quatech_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
 		  struct uart_8250_port *port, int idx)
 {
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return serial_8250_need_ioport(dev);
+
 	/* Needed by pci_quatech calls below */
 	port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags));
 	/* Set up the clocking */
@@ -1864,6 +1885,9 @@ static int kt_serial_setup(struct serial_private *priv,
 			   const struct pciserial_board *board,
 			   struct uart_8250_port *port, int idx)
 {
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return serial_8250_need_ioport(dev);
+
 	port->port.flags |= UPF_BUG_THRE;
 	port->port.serial_in = kt_serial_in;
 	port->port.handle_break = kt_handle_break;
@@ -1918,6 +1942,8 @@ static int pci_wch_ch38x_init(struct pci_dev *dev)
 	int max_port;
 	unsigned long iobase;
 
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return serial_8250_need_ioport(dev);
 
 	switch (dev->device) {
 	case 0x3853: /* 8 ports */
@@ -1937,6 +1963,9 @@ static void pci_wch_ch38x_exit(struct pci_dev *dev)
 {
 	unsigned long iobase;
 
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		serial_8250_need_ioport(dev);
+
 	iobase = pci_resource_start(dev, 0);
 	outb(0x0, iobase + CH384_XINT_ENABLE_REG);
 }
@@ -2052,6 +2081,9 @@ static int pci_moxa_init(struct pci_dev *dev)
 	unsigned int i, num_ports = moxa_get_nports(device);
 	u8 val, init_mode = MOXA_RS232;
 
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return serial_8250_need_ioport(dev);
+
 	if (!(pci_moxa_supported_rs(dev) & MOXA_SUPP_RS232)) {
 		init_mode = MOXA_RS422;
 	}
@@ -2084,6 +2116,9 @@ pci_moxa_setup(struct serial_private *priv,
 	unsigned int bar = FL_GET_BASE(board->flags);
 	int offset;
 
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return serial_8250_need_ioport(dev);
+
 	if (board->num_ports == 4 && idx == 3)
 		offset = 7 * board->uart_offset;
 	else
Niklas Schnelle Oct. 4, 2024, 2:44 p.m. UTC | #19
On Fri, 2024-10-04 at 12:09 +0200, Niklas Schnelle wrote:
> On Wed, 2024-10-02 at 23:59 +0100, Maciej W. Rozycki wrote:
> > On Wed, 2 Oct 2024, Arnd Bergmann wrote:
> > 
---8<---
> > > Part of the problem that Niklas is trying to solve with the
> > > CONFIG_HAS_IOPORT annotations is to prevent an invalid inb()/outb()
> > > from turning into a NULL pointer dereference as it currently does
> > > on architectures that have no way to support PIO but get the
> > > default implementation from asm-generic/io.h.
> > 
> >  It can be worse than that.  Part of my confusion with the defxx driver 
> > trying to do port I/O with my POWER9 system came from the mapping actually 
> > resulting in non-NULL invalid pointers, dereferencing which caused a flood 
> > of obscure messages produced to the system console by the system firmware:
> > 
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > [...]
> > 
> > from which it was all but obvious that they were caused by an attempt to 
> > use PCI port I/O with a system lacking support for it.
> > 
> > > It's not clear if having a silently non-working driver or one
> > > that crashes makes it easier to debug for users. Having a clear
> > > warning message in the PCI probe code is probably the best
> > > we can hope for.
> > 
> >  I agree.  Enthusiastically.
> 
> I think there was also a bit of a misunderstanding. My argument that
> this would be very ugly in the general case was really meant as general
> case outside of drivers like 8250 that deals with both I/O port and
> MMIO i.e. we can't warn/error when !HAS_IOPORT deactivates a whole
> driver because seeing an I/O port BAR in common PCI code doesn't mean
> that it is required for use of the device.
> 
> I'm working on a new proposal for 8250 now. Basically I think we can
> put the warning/error in serial8250_pci_setup_port(). And then for
> those 8250_pci.c "sub drivers" that require I/O ports instead of just
> ifdeffing out their setup/init/exit we can define anything but setup to
> NULL and setup to pci_default_setup() such that the latter will find
> the I/O port BAR via FL_GET_BASE() and subsequently cause the error
> print in serial8250_pci_setup_port(). It's admittedly a bit odd but it
> also keeps the #ifdefs to just around the code that wouldn't compile.

I've now got this to compile on s390x with the !S390 check removed in
Kconfig. Instead of "misusing" pci_default_setup() I instead added a
pci_fail_io_port_setup() helper that prints the error and returns -
EINVAL.

> 
> One thing I'm not happy with yet is the handling around
> is_upf_fourport(port) in 8250_pci.c. With !HAS_IOPORT this is defined
> as false. With that it makes sure that inb_p()/outb_p() aren't called
> but I think this only works because the compiler usually drops the dead
> if clause. I think there were previous discussions around this but I
> think just like IS_ENABLED() checks this isn't quite correct.

It's not the nicest but I added the necessary #ifdefs in 8250_port.c
and at least they are small sections. As Arnd said we will go back to a
single series. So I've also switched to a b4 prep workflow which I
usually use nowadays and so the current code can be found here:

https://git.kernel.org/pub/scm/linux/kernel/git/niks/linux.git/log/?h=b4/has_ioport

I plan to resend on Monday based on v6.12-rc2 which will also include
the bcachefs fix to fix building on Big Endian which I was previously
carrying for my s390x development convenience.

Thanks,
Niklas
Niklas Schnelle Oct. 4, 2024, 4:03 p.m. UTC | #20
On Fri, 2024-10-04 at 12:48 +0000, Arnd Bergmann wrote:
> On Fri, Oct 4, 2024, at 10:09, Niklas Schnelle wrote:
> 
> > I'm working on a new proposal for 8250 now. Basically I think we can
> > put the warning/error in serial8250_pci_setup_port(). And then for
> > those 8250_pci.c "sub drivers" that require I/O ports instead of just
> > ifdeffing out their setup/init/exit we can define anything but setup to
> > NULL and setup to pci_default_setup() such that the latter will find
> > the I/O port BAR via FL_GET_BASE() and subsequently cause the error
> > print in serial8250_pci_setup_port(). It's admittedly a bit odd but it
> > also keeps the #ifdefs to just around the code that wouldn't compile.
> 
> Right, makes sense.
> 
> > One thing I'm not happy with yet is the handling around
> > is_upf_fourport(port) in 8250_pci.c. With !HAS_IOPORT this is defined
> > as false. With that it makes sure that inb_p()/outb_p() aren't called
> > but I think this only works because the compiler usually drops the dead
> > if clause. I think there were previous discussions around this but I
> > think just like IS_ENABLED() checks this isn't quite correct.
> 
> Not sure what you mean, we rely on dead code elimination in all
> kinds of places. The only bit to be aware of is that normal
> 'inline' functions may not get constant-folded all the time,
> but anything that is either a macro or __always_inline does
> work.

Ah ok, didn't know this was okay to rely on. Then I can roll the extra
#ifdefs back. Need to address the test bot's finding anyway. There we
can get #ifdef __i386__ but also !HAS_IOPORT on um.

> 
> I just verified that the version below does what Maciej
> suggested with IS_ENABLED() in 8250-pci, and this works fine.
> 
>        Arnd

Your version below is also pretty nice a bit more spread out but less
#ifdefs. That said at least in my NeoVim setup the #ifdefs are nicely
grayed out via clang-analyzer / lsp which to me actually makes #ifdefs
quite easy to see at a glance but that's probably a niche opinion.

> 
> diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
> index 6709b6a5f301..784190824aad 100644
> --- a/drivers/tty/serial/8250/8250_pci.c
> +++ b/drivers/tty/serial/8250/8250_pci.c
> @@ -928,6 +928,14 @@ static int pci_netmos_init(struct pci_dev *dev)
>  	return num_serial;
>  }
>  
> +static int serial_8250_need_ioport(struct pci_dev *dev)
> +{
> +	dev_warn(&dev->dev,
> +		 "Serial port not supported because of missing I/O resource\n");
> +
> +	return -ENXIO;
> +}
> +
---8<---
Maciej W. Rozycki Oct. 4, 2024, 4:24 p.m. UTC | #21
On Fri, 4 Oct 2024, Arnd Bergmann wrote:

> >  It can be worse than that.  Part of my confusion with the defxx driver 
> > trying to do port I/O with my POWER9 system came from the mapping actually 
> > resulting in non-NULL invalid pointers, dereferencing which caused a flood 
> > of obscure messages produced to the system console by the system firmware:
> >
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > LPC[000]: Got SYNC no-response error. Error address reg: 0xd0010014
> > IPMI: dropping non severe PEL event
> > [...]
> >
> > from which it was all but obvious that they were caused by an attempt to 
> > use PCI port I/O with a system lacking support for it.
> 
> Ah, that's too bad. I think this is a result of the patch that
> Michael did to shut up the NULL pointer warning we get, see
> be140f1732b5 ("powerpc/64: Set _IO_BASE to POISON_POINTER_DELTA
> not 0 for CONFIG_PCI=n"). I really wish we could have finished
> Niklas' series earlier to avoid this.

 That was back in 2020 (5.9.0), so long before be140f1732b5, and it's a 
production system, so I don't want to fiddle with it beyond necessity:

$ uptime
 16:53:27 up 466 days,  9:49,  5 users,  load average: 0.14, 0.12, 0.09
$ 

 Essentially defxx has this code (not changed much since, except that 
`dfx_use_mmio' is now always true for PCI):

	if (!dfx_use_mmio)
		region = request_region(bar_start[0], bar_len[0],
					bdev->driver->name);
	if (!region) {
		dfx_register_res_err(print_name, dfx_use_mmio,
				     bar_start[0], bar_len[0]);
		err = -EBUSY;
		goto err_out_disable;
	}
	/* ... */
	if (dfx_use_mmio) {
		/* ... */
	} else {
		bp->base.port = bar_start[0];
		dev->base_addr = bar_start[0];
	}

so whatever came out of BAR[1]:

pci 0031:02:04.0: [1011:000f] type 00 class 0x020200
pci 0031:02:04.0: reg 0x10: [mem 0x620c080020000-0x620c08002007f]
pci 0031:02:04.0: reg 0x14: [io  0x0000-0x007f]
pci 0031:02:04.0: reg 0x18: [mem 0x620c080030000-0x620c08003ffff]
pci 0031:02:04.0: BAR0 [mem size 0x00000080]: requesting alignment to 0x10000

was supplied to `inl'/`outl' and wreaked havoc.

 First of all I think `request_region', etc. ought to fail in the first 
place with non-port-I/O systems: why does a request for a resource that 
cannot be satisfied succeed?  It would have already solved the issue with 
defxx, making the driver gracefully fail to register the device.  I don't 
know if this has been fixed since.

 Second, rather than relying on a magic mapping in the physical space 
causing a bus error (unless you are absolutely sure it is going to work in 
100% cases), I think it would make sense to make port I/O accessors call 
BUG_ON(no_port_io) explicitly for platforms where it is only known at run 
time whether PCI/e port I/O is available or not.  Port I/O is the opposite 
of performance already, so a couple of extra instructions will be lost in 
the latency and at least POWER has conditional traps, so there'd be no 
branch penalty.

> >  Well, virtually all non-x86 systems continue supporting PCI/e port I/O 
> > via a suitably decoded CPU-side MMIO window, so I think coming across one 
> > that doesn't can still be a nasty surprise even in 2024.  For instance 
> > I've been happily using a PC parallel port PCIe option card, one of the 
> > very few interfaces if not the only one remaining that have not ever seen 
> > an MMIO variant, with my RISC-V hardware, newer than said POWER9 system.
> >
> >  So far it's been the s390 and a couple of POWER system implementations 
> > that have support for PCI/e port I/O removed.  Have I missed anything?
> 
> I meant PCIe cards with I/O space here, not host bridges. I know you
> have a lot of them, but what I've heard from Arm platform maintainers
> is that they tend to struggle finding any PCIe cards to test their
> hsot bridge drivers on, and I expect that a lot of them are actually
> broken because they have never been tested and just copied the
> implementation badly from some other driver.

 I would expect serial ports to be the most common PCIe options still 
using port I/O.  Sadly OxSemi was acquired and their line of devices, 
which support MMIO, cancelled at one point and my observations seem to 
indicate that what is still manufactured uses port I/O (correct me if I'm 
wrong please).  Last time I checked OxSemi-based option cards were still 
available though, but one may have to check with the supplier as to 
whether they have been configured for MMIO or port I/O, as they're not 
dual-mapped.

> I think the only new PCIe devices you can find today that still use
> I/O space are ones with compatibility registers for IBM PC style
> hardware (vga, uart, parport), but most users would never have used
> one of those and instead use the native register interface of their
> GPU (on non-x86), USB-serial and no parport. Other devices that
> needed I/O space never worked on PCIe anyway because of the lack
> of ISA style DMA.

 I think serial port options are the most likely devices still in use, 
given that UARTs continue being widely used in industrial applications.  
Depending on application a USB serial adapter may or may not be suitable 
to interface those.

> There are also a lot of Arm systems that have no I/O space support at
> all, such as the Apple M2 I'm using at the moment.

 Thanks for letting me know.  Is it AArch64 only that has no port I/O 
support in the PCIe root complex nowadays, or is it 32-bit ARM as well?

  Maciej
Maciej W. Rozycki Oct. 4, 2024, 4:34 p.m. UTC | #22
On Fri, 4 Oct 2024, Niklas Schnelle wrote:

> >  Can we just provide dummy prototypes with `__attribute__((error("...")))' 
> > instead?  This will surely prevent us from having to bend backwards so as 
> > to make sure the compiler won't see any spurious references to these 
> > inexistent functions or macros.  We already have a `__compiletime_error()' 
> > macro for this purpose even.
> 
> This is already done in the final patch of my series when disabling
> inb()/outb() and friends.

 Good!

> >  I agree.  Enthusiastically.
> 
> I think there was also a bit of a misunderstanding. My argument that
> this would be very ugly in the general case was really meant as general
> case outside of drivers like 8250 that deals with both I/O port and
> MMIO i.e. we can't warn/error when !HAS_IOPORT deactivates a whole
> driver because seeing an I/O port BAR in common PCI code doesn't mean
> that it is required for use of the device.

 Absolutely.  Just seeing an I/O bar does not mean it's ever going to be 
used.  Even conventional PCI hardware is often dual-mapped and it's up to 
the driver to choose which mapping to use.

> I'm working on a new proposal for 8250 now. Basically I think we can
> put the warning/error in serial8250_pci_setup_port(). And then for
> those 8250_pci.c "sub drivers" that require I/O ports instead of just
> ifdeffing out their setup/init/exit we can define anything but setup to
> NULL and setup to pci_default_setup() such that the latter will find
> the I/O port BAR via FL_GET_BASE() and subsequently cause the error
> print in serial8250_pci_setup_port(). It's admittedly a bit odd but it
> also keeps the #ifdefs to just around the code that wouldn't compile.

 I'd rather you did what Arnd example patch does and just made original 
handlers bail out right away unless IS_ENABLED(CONFIG_HAS_IOPORT).  I do 
hope it will make no #ifdef necessary in 8250_pci.c at all.

  Maciej
Arnd Bergmann Oct. 4, 2024, 4:57 p.m. UTC | #23
On Fri, Oct 4, 2024, at 16:24, Maciej W. Rozycki wrote:
> On Fri, 4 Oct 2024, Arnd Bergmann wrote:
>> There are also a lot of Arm systems that have no I/O space support at
>> all, such as the Apple M2 I'm using at the moment.
>
>  Thanks for letting me know.  Is it AArch64 only that has no port I/O 
> support in the PCIe root complex nowadays, or is it 32-bit ARM as well?

I'm fairly sure we have some on 32-bit as well, it's certainly
up to the specific SoC rather than the architecture.

I think I've seen three different cases:

- Apple leaves out every feature from hardware that they don't
  have to do for compliance, this also includes CPU stuff like
  32-bit mode, big-endian or cacheable PCI mappings. I think IBMs
  ppc64 chips are in a similar situation

- Some devices can probably do I/O space in hardware, but this is
  left out from the driver or the DT because it has not been
  validated to work

- Some devices have a limited number of mapping windows that
  are shared between prefetchable-memory, non-prefetchable-memory,
  config and io space. Leaving out I/O space completely is
  easier than runtime remapping of the windows

      Arnd
Guenter Roeck Nov. 22, 2024, 3:18 p.m. UTC | #24
On Fri, Apr 05, 2024 at 05:29:24PM +0200, Niklas Schnelle wrote:
> In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
> compile time. We thus need to add HAS_IOPORT as dependency for those
> drivers using them unconditionally. For 8250 based drivers some support
> MMIO only use so fence only the parts requiring I/O ports.
> 
> Co-developed-by: Arnd Bergmann <arnd@kernel.org>
> Signed-off-by: Arnd Bergmann <arnd@kernel.org>
> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
...
> @@ -422,10 +443,12 @@ static void set_io_from_upio(struct uart_port *p)
>  	up->dl_write = default_serial_dl_write;
>  
> +	default:
> +		WARN(1, "Unsupported UART type %x\n", p->iotype);

So, according to this patch, the serial uart on microblaze, nios2,
openrisc, xtensa, and possibly others is not or no longer supported.

WARNING: CPU: 0 PID: 0 at drivers/tty/serial/8250/8250_port.c:470 serial8250_set_defaults+0x1a8/0x22c
Unsupported UART type 0

Any special reason ?

Guenter
Niklas Schnelle Nov. 22, 2024, 3:35 p.m. UTC | #25
On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
> On Fri, Apr 05, 2024 at 05:29:24PM +0200, Niklas Schnelle wrote:
> > In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
> > compile time. We thus need to add HAS_IOPORT as dependency for those
> > drivers using them unconditionally. For 8250 based drivers some support
> > MMIO only use so fence only the parts requiring I/O ports.
> > 
> > Co-developed-by: Arnd Bergmann <arnd@kernel.org>
> > Signed-off-by: Arnd Bergmann <arnd@kernel.org>
> > Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> ...
> > @@ -422,10 +443,12 @@ static void set_io_from_upio(struct uart_port *p)
> >  	up->dl_write = default_serial_dl_write;
> >  
> > +	default:
> > +		WARN(1, "Unsupported UART type %x\n", p->iotype);
> 
> So, according to this patch, the serial uart on microblaze, nios2,
> openrisc, xtensa, and possibly others is not or no longer supported.
> 
> WARNING: CPU: 0 PID: 0 at drivers/tty/serial/8250/8250_port.c:470 serial8250_set_defaults+0x1a8/0x22c
> Unsupported UART type 0
> 
> Any special reason ?
> 
> Guenter

So according to the warning the p->iotype is 0 which is UPIO_PORT.
For UPIO_PORT the switch above the WARN would pick io_serial_in() and
io_serial_out() as handlers. These use inb() respectively outb() to
access the serial so I don't see how they would work with !HAS_IOPORT
and it most definitely won't work for s390.

Now for Microblaze Kconfig says to select HAS_IOPORT if PCI so I'd
assume that it can use inb()/outb() and maybe the PCI requirement is
not correct if this isn't a PCI device and it used to work with
inb()/outb()? For nios2, openrisc, and xtensa they don't select
HAS_IOPORT so either it really won't work anyway or they should select
it. Can you tell us more about the devices involved where you saw this?

Thanks,
Niklas
Arnd Bergmann Nov. 22, 2024, 4:31 p.m. UTC | #26
On Fri, Nov 22, 2024, at 16:35, Niklas Schnelle wrote:
> On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
>> On Fri, Apr 05, 2024 at 05:29:24PM +0200, Niklas Schnelle wrote:
>> > In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
>> > compile time. We thus need to add HAS_IOPORT as dependency for those
>> > drivers using them unconditionally. For 8250 based drivers some support
>> > MMIO only use so fence only the parts requiring I/O ports.
>> > 
>> > Co-developed-by: Arnd Bergmann <arnd@kernel.org>
>> > Signed-off-by: Arnd Bergmann <arnd@kernel.org>
>> > Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
>> ...
>> > @@ -422,10 +443,12 @@ static void set_io_from_upio(struct uart_port *p)
>> >  	up->dl_write = default_serial_dl_write;
>> >  
>> > +	default:
>> > +		WARN(1, "Unsupported UART type %x\n", p->iotype);
>> 
>> So, according to this patch, the serial uart on microblaze, nios2,
>> openrisc, xtensa, and possibly others is not or no longer supported.
>> 
>> WARNING: CPU: 0 PID: 0 at drivers/tty/serial/8250/8250_port.c:470 serial8250_set_defaults+0x1a8/0x22c
>> Unsupported UART type 0
>> 
>> Any special reason ?
>
> So according to the warning the p->iotype is 0 which is UPIO_PORT.
> For UPIO_PORT the switch above the WARN would pick io_serial_in() and
> io_serial_out() as handlers. These use inb() respectively outb() to
> access the serial so I don't see how they would work with !HAS_IOPORT
> and it most definitely won't work for s390.
>
> Now for Microblaze Kconfig says to select HAS_IOPORT if PCI so I'd
> assume that it can use inb()/outb() and maybe the PCI requirement is
> not correct if this isn't a PCI device and it used to work with
> inb()/outb()? For nios2, openrisc, and xtensa they don't select
> HAS_IOPORT so either it really won't work anyway or they should select
> it. Can you tell us more about the devices involved where you saw this?

I've tried to have a quick look at the four architectures, here
is what I see in the sources:

- on microblaze, the default uart is xlnx,xps-uartlite-1.00.a,
  and there is no 8250. The PCI code that theoretically supports
  I/O port access through an ISA bridge (copied from powerpc),
  but there is currently no code to set up the I/O space window,
  so in practice the port I/O is just a NULL pointer dereference.

- nios2 has a 16550 compatible UART listed in the devicetree
  file, but this uses normal UPIO_MEM registers, not ISA-style
  I/O ports. There is no support for ISA or PCI.

- openrisc has no support for port I/O, like on nios2 the
  16550 style uart is on UPIO_MEM according to the devicetree

- xtensa manually sets up a UPIO_MEM uart in its board files
  and in its dts files. The PCI support does have code to
  hand port I/O, but it looks incorrect to me and either broke
  at some point or never worked. Most likely this was copied
  incorrectly from old powerpc or sparc code where it worked.

So in all four cases, the normal uart should keep working,
and if something tried to start up an ISA style 8250, I
would expect to see the new version produce the WARN()
in place of what was a NULL pointer dereference (reading
from (u8 *)0x2f8) before.

Since there are so many ways to set up a broken 8250,
it is still possible that something tries to add another
UPIO_PORT uart, and that this causes the WARN() to trigger,
if we find any of those, the fix is to no stop registering
broken ports.

     Arnd
Guenter Roeck Nov. 22, 2024, 5:07 p.m. UTC | #27
On 11/22/24 07:35, Niklas Schnelle wrote:
> On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
>> On Fri, Apr 05, 2024 at 05:29:24PM +0200, Niklas Schnelle wrote:
>>> In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
>>> compile time. We thus need to add HAS_IOPORT as dependency for those
>>> drivers using them unconditionally. For 8250 based drivers some support
>>> MMIO only use so fence only the parts requiring I/O ports.
>>>
>>> Co-developed-by: Arnd Bergmann <arnd@kernel.org>
>>> Signed-off-by: Arnd Bergmann <arnd@kernel.org>
>>> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
>> ...
>>> @@ -422,10 +443,12 @@ static void set_io_from_upio(struct uart_port *p)
>>>   	up->dl_write = default_serial_dl_write;
>>>   
>>> +	default:
>>> +		WARN(1, "Unsupported UART type %x\n", p->iotype);
>>
>> So, according to this patch, the serial uart on microblaze, nios2,
>> openrisc, xtensa, and possibly others is not or no longer supported.
>>
>> WARNING: CPU: 0 PID: 0 at drivers/tty/serial/8250/8250_port.c:470 serial8250_set_defaults+0x1a8/0x22c
>> Unsupported UART type 0
>>
>> Any special reason ?
>>
>> Guenter
> 
> So according to the warning the p->iotype is 0 which is UPIO_PORT.
> For UPIO_PORT the switch above the WARN would pick io_serial_in() and
> io_serial_out() as handlers. These use inb() respectively outb() to
> access the serial so I don't see how they would work with !HAS_IOPORT
> and it most definitely won't work for s390.
> 
> Now for Microblaze Kconfig says to select HAS_IOPORT if PCI so I'd
> assume that it can use inb()/outb() and maybe the PCI requirement is
> not correct if this isn't a PCI device and it used to work with
> inb()/outb()? For nios2, openrisc, and xtensa they don't select
> HAS_IOPORT so either it really won't work anyway or they should select
> it. Can you tell us more about the devices involved where you saw this?
> 

This is seen when booting the affected architectures with qemu.

Logs:

https://kerneltests.org/builders/qemu-microblaze-master/builds/327/steps/qemubuildcommand/logs/stdio
https://kerneltests.org/builders/qemu-nios2-master/builds/314/steps/qemubuildcommand/logs/stdio
https://kerneltests.org/builders/qemu-openrisc-master/builds/301
https://kerneltests.org/builders/qemu-xtensa-master/builds/311/steps/qemubuildcommand/logs/stdio

Guenter
Guenter Roeck Nov. 22, 2024, 5:22 p.m. UTC | #28
On 11/22/24 08:31, Arnd Bergmann wrote:
> On Fri, Nov 22, 2024, at 16:35, Niklas Schnelle wrote:
>> On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
>>> On Fri, Apr 05, 2024 at 05:29:24PM +0200, Niklas Schnelle wrote:
>>>> In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at
>>>> compile time. We thus need to add HAS_IOPORT as dependency for those
>>>> drivers using them unconditionally. For 8250 based drivers some support
>>>> MMIO only use so fence only the parts requiring I/O ports.
>>>>
>>>> Co-developed-by: Arnd Bergmann <arnd@kernel.org>
>>>> Signed-off-by: Arnd Bergmann <arnd@kernel.org>
>>>> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
>>> ...
>>>> @@ -422,10 +443,12 @@ static void set_io_from_upio(struct uart_port *p)
>>>>   	up->dl_write = default_serial_dl_write;
>>>>   
>>>> +	default:
>>>> +		WARN(1, "Unsupported UART type %x\n", p->iotype);
>>>
>>> So, according to this patch, the serial uart on microblaze, nios2,
>>> openrisc, xtensa, and possibly others is not or no longer supported.
>>>
>>> WARNING: CPU: 0 PID: 0 at drivers/tty/serial/8250/8250_port.c:470 serial8250_set_defaults+0x1a8/0x22c
>>> Unsupported UART type 0
>>>
>>> Any special reason ?
>>
>> So according to the warning the p->iotype is 0 which is UPIO_PORT.
>> For UPIO_PORT the switch above the WARN would pick io_serial_in() and
>> io_serial_out() as handlers. These use inb() respectively outb() to
>> access the serial so I don't see how they would work with !HAS_IOPORT
>> and it most definitely won't work for s390.
>>
>> Now for Microblaze Kconfig says to select HAS_IOPORT if PCI so I'd
>> assume that it can use inb()/outb() and maybe the PCI requirement is
>> not correct if this isn't a PCI device and it used to work with
>> inb()/outb()? For nios2, openrisc, and xtensa they don't select
>> HAS_IOPORT so either it really won't work anyway or they should select
>> it. Can you tell us more about the devices involved where you saw this?
> 
> I've tried to have a quick look at the four architectures, here
> is what I see in the sources:
> 
> - on microblaze, the default uart is xlnx,xps-uartlite-1.00.a,
>    and there is no 8250. The PCI code that theoretically supports
>    I/O port access through an ISA bridge (copied from powerpc),
>    but there is currently no code to set up the I/O space window,
>    so in practice the port I/O is just a NULL pointer dereference.
> 
> - nios2 has a 16550 compatible UART listed in the devicetree
>    file, but this uses normal UPIO_MEM registers, not ISA-style
>    I/O ports. There is no support for ISA or PCI.
> 
> - openrisc has no support for port I/O, like on nios2 the
>    16550 style uart is on UPIO_MEM according to the devicetree
> 
> - xtensa manually sets up a UPIO_MEM uart in its board files
>    and in its dts files. The PCI support does have code to
>    hand port I/O, but it looks incorrect to me and either broke
>    at some point or never worked. Most likely this was copied
>    incorrectly from old powerpc or sparc code where it worked.
> 
> So in all four cases, the normal uart should keep working,
> and if something tried to start up an ISA style 8250, I
> would expect to see the new version produce the WARN()
> in place of what was a NULL pointer dereference (reading
> from (u8 *)0x2f8) before.
> 
> Since there are so many ways to set up a broken 8250,
> it is still possible that something tries to add another
> UPIO_PORT uart, and that this causes the WARN() to trigger,
> if we find any of those, the fix is to no stop registering
> broken ports.
> 

The call chain in all cases is

[    0.013596] Call Trace:
[    0.013796]  [<d06eb249>] dump_stack+0x9/0xc
[    0.014115]  [<d000c12c>] __warn+0x94/0xbc
[    0.014332]  [<d000c29c>] warn_slowpath_fmt+0x148/0x184
[    0.014560]  [<d04f03d8>] set_io_from_upio+0x70/0x98
[    0.014781]  [<d04f0459>] serial8250_set_defaults+0x59/0x8c
[    0.015013]  [<d04efa6a>] serial8250_setup_port+0x6e/0x90
[    0.015240]  [<d0ae2a12>] serial8250_isa_init_ports+0x32/0x5c
[    0.015473]  [<d0ae28a1>] univ8250_console_init+0x15/0x24
[    0.015698]  [<d0ad0684>] console_init+0x18/0x20
[    0.015926]  [<d0acbf43>] start_kernel+0x3db/0x4cc
[    0.016145]  [<d06ebc37>] _startup+0x13b/0x13b

That seems unconditional. What is the architecture expected to do to
prevent this call chain from being executed ?

Guenter
Arnd Bergmann Nov. 22, 2024, 7:24 p.m. UTC | #29
On Fri, Nov 22, 2024, at 18:22, Guenter Roeck wrote:
> On 11/22/24 08:31, Arnd Bergmann wrote:
>> On Fri, Nov 22, 2024, at 16:35, Niklas Schnelle wrote:
>>> On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
>> So in all four cases, the normal uart should keep working,
>> and if something tried to start up an ISA style 8250, I
>> would expect to see the new version produce the WARN()
>> in place of what was a NULL pointer dereference (reading
>> from (u8 *)0x2f8) before.
>> 
>> Since there are so many ways to set up a broken 8250,
>> it is still possible that something tries to add another
>> UPIO_PORT uart, and that this causes the WARN() to trigger,
>> if we find any of those, the fix is to no stop registering
>> broken ports.
>> 
>
> The call chain in all cases is
>
> [    0.013596] Call Trace:
> [    0.013796]  [<d06eb249>] dump_stack+0x9/0xc
> [    0.014115]  [<d000c12c>] __warn+0x94/0xbc
> [    0.014332]  [<d000c29c>] warn_slowpath_fmt+0x148/0x184
> [    0.014560]  [<d04f03d8>] set_io_from_upio+0x70/0x98
> [    0.014781]  [<d04f0459>] serial8250_set_defaults+0x59/0x8c
> [    0.015013]  [<d04efa6a>] serial8250_setup_port+0x6e/0x90
> [    0.015240]  [<d0ae2a12>] serial8250_isa_init_ports+0x32/0x5c
> [    0.015473]  [<d0ae28a1>] univ8250_console_init+0x15/0x24
> [    0.015698]  [<d0ad0684>] console_init+0x18/0x20
> [    0.015926]  [<d0acbf43>] start_kernel+0x3db/0x4cc
> [    0.016145]  [<d06ebc37>] _startup+0x13b/0x13b
>
> That seems unconditional. What is the architecture expected to do to
> prevent this call chain from being executed ?

This looks like a bug in drivers/tty/serial/8250/8250_platform.c
to me, not something the architectures did wrong. My impression
from __serial8250_isa_init_ports() is that this is supposed
to initialize exactly the ports in the old_serial_port[]
array, which is filled only on alpha, m68k and x86 but not
on the other ones.

Andy moved this code in ffd8e8bd26e9 ("serial: 8250: Extract
platform driver"), but I don't think this move by itself
changed anything.

serial8250_setup_port() is where it ends up using the
uninitialized serial8250_ports[index] contents. Since 
port->iotype is not set to anything here, it defaults to
'0', which happens to be UPIO_PORT.

The reason it doesn't immediately crash and burn is that
this is still only setting up the structures for later
use, but I assume that trying to use console=ttyS0, or
opening /dev/ttyS0 on the uninitialized port would
then cause an oops.

The bit I'm less sure about is why the
serial8250_setup_port() function is called here in
the first place. I assume it does something for
/some/ architecture, but it's clearly wrong for
most of them.

       Arnd
Guenter Roeck Nov. 22, 2024, 8:44 p.m. UTC | #30
On 11/22/24 11:24, Arnd Bergmann wrote:
> On Fri, Nov 22, 2024, at 18:22, Guenter Roeck wrote:
>> On 11/22/24 08:31, Arnd Bergmann wrote:
>>> On Fri, Nov 22, 2024, at 16:35, Niklas Schnelle wrote:
>>>> On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
>>> So in all four cases, the normal uart should keep working,
>>> and if something tried to start up an ISA style 8250, I
>>> would expect to see the new version produce the WARN()
>>> in place of what was a NULL pointer dereference (reading
>>> from (u8 *)0x2f8) before.
>>>
>>> Since there are so many ways to set up a broken 8250,
>>> it is still possible that something tries to add another
>>> UPIO_PORT uart, and that this causes the WARN() to trigger,
>>> if we find any of those, the fix is to no stop registering
>>> broken ports.
>>>
>>
>> The call chain in all cases is
>>
>> [    0.013596] Call Trace:
>> [    0.013796]  [<d06eb249>] dump_stack+0x9/0xc
>> [    0.014115]  [<d000c12c>] __warn+0x94/0xbc
>> [    0.014332]  [<d000c29c>] warn_slowpath_fmt+0x148/0x184
>> [    0.014560]  [<d04f03d8>] set_io_from_upio+0x70/0x98
>> [    0.014781]  [<d04f0459>] serial8250_set_defaults+0x59/0x8c
>> [    0.015013]  [<d04efa6a>] serial8250_setup_port+0x6e/0x90
>> [    0.015240]  [<d0ae2a12>] serial8250_isa_init_ports+0x32/0x5c
>> [    0.015473]  [<d0ae28a1>] univ8250_console_init+0x15/0x24
>> [    0.015698]  [<d0ad0684>] console_init+0x18/0x20
>> [    0.015926]  [<d0acbf43>] start_kernel+0x3db/0x4cc
>> [    0.016145]  [<d06ebc37>] _startup+0x13b/0x13b
>>
>> That seems unconditional. What is the architecture expected to do to
>> prevent this call chain from being executed ?
> 
> This looks like a bug in drivers/tty/serial/8250/8250_platform.c
> to me, not something the architectures did wrong. My impression
> from __serial8250_isa_init_ports() is that this is supposed
> to initialize exactly the ports in the old_serial_port[]
> array, which is filled only on alpha, m68k and x86 but not
> on the other ones.
> 
> Andy moved this code in ffd8e8bd26e9 ("serial: 8250: Extract
> platform driver"), but I don't think this move by itself
> changed anything.
> 
> serial8250_setup_port() is where it ends up using the
> uninitialized serial8250_ports[index] contents. Since
> port->iotype is not set to anything here, it defaults to
> '0', which happens to be UPIO_PORT.
> 
> The reason it doesn't immediately crash and burn is that
> this is still only setting up the structures for later
> use, but I assume that trying to use console=ttyS0, or
> opening /dev/ttyS0 on the uninitialized port would
> then cause an oops.
> 

All four affected platforms use ttyS0, only it is mmio based,
not io port based.

Guenter

> The bit I'm less sure about is why the
> serial8250_setup_port() function is called here in
> the first place. I assume it does something for
> /some/ architecture, but it's clearly wrong for
> most of them.
> 
>         Arnd
Arnd Bergmann Nov. 22, 2024, 10:51 p.m. UTC | #31
On Fri, Nov 22, 2024, at 21:44, Guenter Roeck wrote:
> On 11/22/24 11:24, Arnd Bergmann wrote:
>> 
>> serial8250_setup_port() is where it ends up using the
>> uninitialized serial8250_ports[index] contents. Since
>> port->iotype is not set to anything here, it defaults to
>> '0', which happens to be UPIO_PORT.
>> 
>> The reason it doesn't immediately crash and burn is that
>> this is still only setting up the structures for later
>> use, but I assume that trying to use console=ttyS0, or
>> opening /dev/ttyS0 on the uninitialized port would
>> then cause an oops.
>> 
>
> All four affected platforms use ttyS0, only it is mmio based,
> not io port based.

Right, so I think the problem is really the 8250_platform.c
file trying to handle all possible cases on x86 (acpi,
isapnp, legacy isa) that may define the same UARTs, but
also trying to handle non-legacy ports with their own
special cases on other architectures.

The patch below is a first idea of how we can skip the
legacy ISA case on architectures that don't define those.
If this works, we can try to come up with a better way of
doing that. Ideally all the pre-DT boardfile machines that
define their own "serial8250" platform_device (with platform_data)
should use a different identifier the x86-legacy case that
uses the platform_device (without platform_data). Similarly,
I think the riscv special ACPI port needs its own trivial
driver without all the ISA magic.

      Arnd

diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c
index 66fd6d5d4835..610b31517734 100644
--- a/drivers/tty/serial/8250/8250_platform.c
+++ b/drivers/tty/serial/8250/8250_platform.c
@@ -101,7 +101,8 @@ static void __init __serial8250_isa_init_ports(void)
 
 void __init serial8250_isa_init_ports(void)
 {
-	DO_ONCE(__serial8250_isa_init_ports);
+	if (ARRAY_SIZE(old_serial_port))
+		DO_ONCE(__serial8250_isa_init_ports);
 }
 
 /*
@@ -283,7 +284,7 @@ static const struct acpi_device_id acpi_platform_serial_table[] = {
 };
 MODULE_DEVICE_TABLE(acpi, acpi_platform_serial_table);
 
-static struct platform_driver serial8250_isa_driver = {
+static struct platform_driver serial8250_platform_driver = {
 	.probe		= serial8250_probe,
 	.remove		= serial8250_remove,
 	.suspend	= serial8250_suspend,
@@ -300,7 +301,7 @@ static struct platform_driver serial8250_isa_driver = {
  */
 struct platform_device *serial8250_isa_devs;
 
-static int __init serial8250_init(void)
+static int __init serial8250_isa_init(void)
 {
 	int ret;
 
@@ -337,11 +338,8 @@ static int __init serial8250_init(void)
 
 	serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
 
-	ret = platform_driver_register(&serial8250_isa_driver);
-	if (ret == 0)
-		goto out;
+	return 0;
 
-	platform_device_del(serial8250_isa_devs);
 put_dev:
 	platform_device_put(serial8250_isa_devs);
 unreg_pnp:
@@ -355,6 +353,17 @@ static int __init serial8250_init(void)
 out:
 	return ret;
 }
+
+static int __init serial8250_init(void)
+{
+	if (ARRAY_SIZE(old_serial_port)) {
+		int ret = serial8250_isa_init();
+		if (ret)
+			return ret;
+	}
+
+	return platform_driver_register(&serial8250_platform_driver);
+}
 module_init(serial8250_init);
 
 static void __exit serial8250_exit(void)
@@ -368,7 +377,11 @@ static void __exit serial8250_exit(void)
 	 */
 	serial8250_isa_devs = NULL;
 
-	platform_driver_unregister(&serial8250_isa_driver);
+	platform_driver_unregister(&serial8250_platform_driver);
+
+	if (!ARRAY_SIZE(old_serial_port))
+		return;
+
 	platform_device_unregister(isa_dev);
 
 	serial8250_pnp_exit();
Niklas Schnelle Nov. 22, 2024, 11:27 p.m. UTC | #32
On Fri, 2024-11-22 at 09:07 -0800, Guenter Roeck wrote:
> On 11/22/24 07:35, Niklas Schnelle wrote:
> > On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
> > > On Fri, Apr 05, 2024 at 05:29:24PM +0200, Niklas Schnelle wrote:
> > > > In a future patch HAS_IOPORT=n will disable inb()/outb() and
> > > > friends at
> > > > compile time. We thus need to add HAS_IOPORT as dependency for
> > > > those
> > > > drivers using them unconditionally. For 8250 based drivers some
> > > > support
> > > > MMIO only use so fence only the parts requiring I/O ports.
> > > > 
> > > > Co-developed-by: Arnd Bergmann <arnd@kernel.org>
> > > > Signed-off-by: Arnd Bergmann <arnd@kernel.org>
> > > > Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> > > ...
> > > > @@ -422,10 +443,12 @@ static void set_io_from_upio(struct
> > > > uart_port *p)
> > > >   	up->dl_write = default_serial_dl_write;
> > > >   
> > > > +	default:
> > > > +		WARN(1, "Unsupported UART type %x\n", p-
> > > > >iotype);
> > > 
> > > So, according to this patch, the serial uart on microblaze,
> > > nios2,
> > > openrisc, xtensa, and possibly others is not or no longer
> > > supported.
> > > 
> > > WARNING: CPU: 0 PID: 0 at drivers/tty/serial/8250/8250_port.c:470
> > > serial8250_set_defaults+0x1a8/0x22c
> > > Unsupported UART type 0
> > > 
> > > Any special reason ?
> > > 
> > > Guenter
> > 
> > So according to the warning the p->iotype is 0 which is UPIO_PORT.
> > For UPIO_PORT the switch above the WARN would pick io_serial_in()
> > and
> > io_serial_out() as handlers. These use inb() respectively outb() to
> > access the serial so I don't see how they would work with
> > !HAS_IOPORT
> > and it most definitely won't work for s390.
> > 
> > Now for Microblaze Kconfig says to select HAS_IOPORT if PCI so I'd
> > assume that it can use inb()/outb() and maybe the PCI requirement
> > is
> > not correct if this isn't a PCI device and it used to work with
> > inb()/outb()? For nios2, openrisc, and xtensa they don't select
> > HAS_IOPORT so either it really won't work anyway or they should
> > select
> > it. Can you tell us more about the devices involved where you saw
> > this?
> > 
> 
> This is seen when booting the affected architectures with qemu.
> 
> Logs:
> 
> https://kerneltests.org/builders/qemu-microblaze-master/builds/327/steps/qemubuildcommand/logs/stdio
> https://kerneltests.org/builders/qemu-nios2-master/builds/314/steps/qemubuildcommand/logs/stdio
> https://kerneltests.org/builders/qemu-openrisc-master/builds/301
> https://kerneltests.org/builders/qemu-xtensa-master/builds/311/steps/qemubuildcommand/logs/stdio
> 
> Guenter

Am I seeing it right that despite the warning and the code setting
no_serial_in / no_serial_out the console=ttyS0 in the above qemu boots
still worked? Also for example in the nios2 case I see the warning 4
times. So this makes me wonder since the warning is new is it possible
that set_io_from_upio() has been called with an invalid / all
zero port before but it was invisible.

The way I'm reading __serial8250_isa_init_ports() and in particular the
first loop if nr_uarts is e.g. 5 in the nios case but only the first
entry in serial8250_ports[] has the IOMEM 8250 it will still call
serial8250_setup_port() on the 4 other unintalized/zero elements which
would explain the iotype being 0. And as far as I can see nr_uarts is
just set to the value of CONFIG_SERIAL_8250_RUNTIME_UARTS in
8250_platform.c.

I may be totally off though this console_init() stuff has me a little
confused and it's been a long day.

Regards,
Niklas
Niklas Schnelle Nov. 22, 2024, 11:34 p.m. UTC | #33
On Fri, 2024-11-22 at 09:07 -0800, Guenter Roeck wrote:
> On 11/22/24 07:35, Niklas Schnelle wrote:
> > On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:
> > > On Fri, Apr 05, 2024 at 05:29:24PM +0200, Niklas Schnelle wrote:
> > > > In a future patch HAS_IOPORT=n will disable inb()/outb() and
> > > > friends at
> > > > compile time. We thus need to add HAS_IOPORT as dependency for
> > > > those
> > > > drivers using them unconditionally. For 8250 based drivers some
> > > > support
> > > > MMIO only use so fence only the parts requiring I/O ports.
> > > > 
> > > > Co-developed-by: Arnd Bergmann <arnd@kernel.org>
> > > > Signed-off-by: Arnd Bergmann <arnd@kernel.org>
> > > > Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
> > > ...
> > > > @@ -422,10 +443,12 @@ static void set_io_from_upio(struct
> > > > uart_port *p)
> > > >   	up->dl_write = default_serial_dl_write;
> > > >   
> > > > +	default:
> > > > +		WARN(1, "Unsupported UART type %x\n", p-
> > > > >iotype);
> > > 
> > > So, according to this patch, the serial uart on microblaze,
> > > nios2,
> > > openrisc, xtensa, and possibly others is not or no longer
> > > supported.
> > > 
> > > WARNING: CPU: 0 PID: 0 at drivers/tty/serial/8250/8250_port.c:470
> > > serial8250_set_defaults+0x1a8/0x22c
> > > Unsupported UART type 0
> > > 
> > > Any special reason ?
> > > 
> > > Guenter
> > 
> > So according to the warning the p->iotype is 0 which is UPIO_PORT.
> > For UPIO_PORT the switch above the WARN would pick io_serial_in()
> > and
> > io_serial_out() as handlers. These use inb() respectively outb() to
> > access the serial so I don't see how they would work with
> > !HAS_IOPORT
> > and it most definitely won't work for s390.
> > 
> > Now for Microblaze Kconfig says to select HAS_IOPORT if PCI so I'd
> > assume that it can use inb()/outb() and maybe the PCI requirement
> > is
> > not correct if this isn't a PCI device and it used to work with
> > inb()/outb()? For nios2, openrisc, and xtensa they don't select
> > HAS_IOPORT so either it really won't work anyway or they should
> > select
> > it. Can you tell us more about the devices involved where you saw
> > this?
> > 
> 
> This is seen when booting the affected architectures with qemu.
> 
> Logs:
> 
> https://kerneltests.org/builders/qemu-microblaze-master/builds/327/steps/qemubuildcommand/logs/stdio
> https://kerneltests.org/builders/qemu-nios2-master/builds/314/steps/qemubuildcommand/logs/stdio
> https://kerneltests.org/builders/qemu-openrisc-master/builds/301
> https://kerneltests.org/builders/qemu-xtensa-master/builds/311/steps/qemubuildcommand/logs/stdio
> 
> Guenter
> 

Am I seeing it right that despite the warning and the code setting
no_serial_in / no_serial_out the console=ttyS0 in the above qemu boots
still worked? Also for example in the nios2 case I see the warning 4
times. So this makes me wonder since the warning is new is it possible
that set_io_from_upio() has been called with an invalid / all
zero port before but it was invisible.

The way I'm reading __serial8250_isa_init_ports() and in particular the
first loop if nr_uarts is e.g. 5 in the nios case but only the first
entry in serial8250_ports[] has the IOMEM 8250 it will still call
serial8250_setup_port() on the 4 other unintalized/zero elements which
would explain the iotype being 0. And as far as I can see nr_uarts is
just set to the value of CONFIG_SERIAL_8250_RUNTIME_UARTS in
8250_platform.c.

I may be totally off though this console_init() stuff has me a little
confused and it's been a long day.

Regards,
Niklas
Guenter Roeck Nov. 23, 2024, 2:14 a.m. UTC | #34
On 11/22/24 14:51, Arnd Bergmann wrote:
> On Fri, Nov 22, 2024, at 21:44, Guenter Roeck wrote:
>> On 11/22/24 11:24, Arnd Bergmann wrote:
>>>
>>> serial8250_setup_port() is where it ends up using the
>>> uninitialized serial8250_ports[index] contents. Since
>>> port->iotype is not set to anything here, it defaults to
>>> '0', which happens to be UPIO_PORT.
>>>
>>> The reason it doesn't immediately crash and burn is that
>>> this is still only setting up the structures for later
>>> use, but I assume that trying to use console=ttyS0, or
>>> opening /dev/ttyS0 on the uninitialized port would
>>> then cause an oops.
>>>
>>
>> All four affected platforms use ttyS0, only it is mmio based,
>> not io port based.
> 
> Right, so I think the problem is really the 8250_platform.c
> file trying to handle all possible cases on x86 (acpi,
> isapnp, legacy isa) that may define the same UARTs, but
> also trying to handle non-legacy ports with their own
> special cases on other architectures.
> 
> The patch below is a first idea of how we can skip the
> legacy ISA case on architectures that don't define those.
> If this works, we can try to come up with a better way of
> doing that. Ideally all the pre-DT boardfile machines that
> define their own "serial8250" platform_device (with platform_data)
> should use a different identifier the x86-legacy case that
> uses the platform_device (without platform_data). Similarly,
> I think the riscv special ACPI port needs its own trivial
> driver without all the ISA magic.
> 
>        Arnd
> 
> diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c
> index 66fd6d5d4835..610b31517734 100644
> --- a/drivers/tty/serial/8250/8250_platform.c
> +++ b/drivers/tty/serial/8250/8250_platform.c

That didn't work.



[    0.005735] Console: colour dummy device 80x25
[    0.006393] BUG: spinlock bad magic on CPU#0, swapper/0
[    0.006459]  lock: serial8250_ports+0x0/0x980, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
[    0.007102] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.12.0-07881-g6206e39999ae #1
[    0.007359] Stack:
[    0.007671] > 00000100 00000000 d09f9e10 d0951c2c
[    0.007765]   d0951c2c 00000000 00000000 d0b06170
[    0.007841]   9003f5ee d09f9e60 00000055 d0dd9900
[    0.007884]   00000000 00000004 d09f9e40 00000000
[    0.007925]   9003f681 d09f9e80 d0dd9900 d08f5924
[    0.007976] > 00000000 d08f5890 ffffffff 00000000
[    0.008019]   906f66b9 d09f9ea0 d0dd9900 00000000
[    0.008059] > 00000000 00000480 00000000 d0a06d74
[    0.008099]   904efe6a d09f9ec0 d0dd9900 00060300
[    0.008139] > 00000000 00000000 00000001 00000000
[    0.008180]   9004368d d09f9ef0 d0aa7238 d7ff70a4
[    0.008220] > d04efe6a 00000000 00000001 00000000
[    0.008260]   00000000 00000000 00000001 00000000
[    0.008301]   90044a84 d09f9f10 d0aa7238 d04efe14
[    0.008340] > d0dd9900 000000c4 d0a06d74 d0a00400
[    0.008381]   90ae6710 d09f9f30 d0aa7238 d0cedea0
[    0.008443] Call Trace:
[    0.008502]  [<d06ed63d>] dump_stack+0x9/0xc
[    0.008624]  [<d003f5ee>] spin_dump+0x4a/0x78
[    0.008677]  [<d003f681>] do_raw_spin_lock+0x1d/0x68
[    0.008727]  [<d06f66b9>] _raw_spin_lock_irqsave+0x41/0x54
[    0.008780]  [<d04efe6a>] univ8250_console_setup+0x56/0x104
[    0.008829]  [<d004368d>] try_enable_preferred_console+0x75/0xf4
[    0.008877]  [<d0044a84>] register_console+0xf4/0x3e4
[    0.008923]  [<d0ae6710>] univ8250_console_init+0x20/0x24
[    0.008970]  [<d0ad44f0>] console_init+0x18/0x20
[    0.009018]  [<d0acfdaf>] start_kernel+0x3db/0x4cc
[    0.009063]  [<d06ee02b>] _startup+0x13b/0x13b

That was xtensa. Others are just silent.

Guenter
Arnd Bergmann Nov. 23, 2024, 9:21 a.m. UTC | #35
On Sat, Nov 23, 2024, at 00:34, Niklas Schnelle wrote:
>
> Am I seeing it right that despite the warning and the code setting
> no_serial_in / no_serial_out the console=ttyS0 in the above qemu boots
> still worked? Also for example in the nios2 case I see the warning 4
> times. So this makes me wonder since the warning is new is it possible
> that set_io_from_upio() has been called with an invalid / all
> zero port before but it was invisible.

Yes, that is certainly the case, sorry if I wasn't clear about this.

The warning shows that there is something wrong with the code,
but that problem has likely existed for a long time. We can
obviously just hide that warning again and ignore the underlying
problem without causing any regressions to the previous state,
but I hope we can improve it in some way that makes it less
broken on non-x86 architectures.

> The way I'm reading __serial8250_isa_init_ports() and in particular the
> first loop if nr_uarts is e.g. 5 in the nios case but only the first
> entry in serial8250_ports[] has the IOMEM 8250 it will still call
> serial8250_setup_port() on the 4 other unintalized/zero elements which
> would explain the iotype being 0. And as far as I can see nr_uarts is
> just set to the value of CONFIG_SERIAL_8250_RUNTIME_UARTS in
> 8250_platform.c.

The way I was reading the code, all five would be uninitialized
at the time we call __serial8250_isa_init_ports(), and the first
port only gets set later on.

Another thing I see is that the 8250 driver ("serial") is the
only one that ends up registering a lot of ports at boot time,
while the other ones only register the ones they actually detect.

E.g. on my Apple workstation, I get

# head /proc/tty/driver/*
==> /proc/tty/driver/IMX-uart <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/fsl-linflexuart <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/fsl-lpuart <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/msm_serial <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/mvebu_serial <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/qcom_geni_console <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/qcom_geni_uart <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/s3c2410_serial <==
serinfo:1.0 driver revision:
0: uart:APPLE S5L mmio:0x39B200000 irq:42 tx:0 rx:0 CTS|DSR|CD

==> /proc/tty/driver/serial <==
serinfo:1.0 driver revision:
0: uart:unknown port:00000000 irq:0
1: uart:unknown port:00000000 irq:0
2: uart:unknown port:00000000 irq:0
3: uart:unknown port:00000000 irq:0
4: uart:unknown port:00000000 irq:0
5: uart:unknown port:00000000 irq:0
6: uart:unknown port:00000000 irq:0
7: uart:unknown port:00000000 irq:0
8: uart:unknown port:00000000 irq:0

==> /proc/tty/driver/tegra_hsuart <==
serinfo:1.0 driver revision:

==> /proc/tty/driver/usbserial <==
usbserinfo:1.0 driver:2.0

The way that this driver was meant to handle that originally
is that /sbin/setserial can be used at runtime to configure
any UART that is attached to an ISA bus even if it did not
get detected at boot time, by setting the correct port and
IRQ numbers from userspace. This may be required on i486
systems with ISA cards providing additional non-PNP 8250
on a nonstandard port number, but is likely not useful on
anything other than x86-32.

      Arnd
Andy Shevchenko Nov. 25, 2024, 7:55 a.m. UTC | #36
On Fri, Nov 22, 2024 at 08:24:37PM +0100, Arnd Bergmann wrote:
> On Fri, Nov 22, 2024, at 18:22, Guenter Roeck wrote:
> > On 11/22/24 08:31, Arnd Bergmann wrote:
> >> On Fri, Nov 22, 2024, at 16:35, Niklas Schnelle wrote:
> >>> On Fri, 2024-11-22 at 07:18 -0800, Guenter Roeck wrote:

...

> >> So in all four cases, the normal uart should keep working,
> >> and if something tried to start up an ISA style 8250, I
> >> would expect to see the new version produce the WARN()
> >> in place of what was a NULL pointer dereference (reading
> >> from (u8 *)0x2f8) before.
> >> 
> >> Since there are so many ways to set up a broken 8250,
> >> it is still possible that something tries to add another
> >> UPIO_PORT uart, and that this causes the WARN() to trigger,
> >> if we find any of those, the fix is to no stop registering
> >> broken ports.
> >
> > The call chain in all cases is
> >
> > [    0.013596] Call Trace:
> > [    0.013796]  [<d06eb249>] dump_stack+0x9/0xc
> > [    0.014115]  [<d000c12c>] __warn+0x94/0xbc
> > [    0.014332]  [<d000c29c>] warn_slowpath_fmt+0x148/0x184
> > [    0.014560]  [<d04f03d8>] set_io_from_upio+0x70/0x98
> > [    0.014781]  [<d04f0459>] serial8250_set_defaults+0x59/0x8c
> > [    0.015013]  [<d04efa6a>] serial8250_setup_port+0x6e/0x90
> > [    0.015240]  [<d0ae2a12>] serial8250_isa_init_ports+0x32/0x5c
> > [    0.015473]  [<d0ae28a1>] univ8250_console_init+0x15/0x24
> > [    0.015698]  [<d0ad0684>] console_init+0x18/0x20
> > [    0.015926]  [<d0acbf43>] start_kernel+0x3db/0x4cc
> > [    0.016145]  [<d06ebc37>] _startup+0x13b/0x13b
> >
> > That seems unconditional. What is the architecture expected to do to
> > prevent this call chain from being executed ?
> 
> This looks like a bug in drivers/tty/serial/8250/8250_platform.c
> to me, not something the architectures did wrong. My impression
> from __serial8250_isa_init_ports() is that this is supposed
> to initialize exactly the ports in the old_serial_port[]
> array, which is filled only on alpha, m68k and x86 but not
> on the other ones.

> Andy moved this code in ffd8e8bd26e9 ("serial: 8250: Extract
> platform driver"), but I don't think this move by itself
> changed anything.

Yep. the idea was to purely split this code out of the core
library parts. I was not intending any functional changes.

I believe it's a preexisted bug, but if you can point out to
the breakage I made, please do it, so I would be able to fix
ASAP.

> serial8250_setup_port() is where it ends up using the
> uninitialized serial8250_ports[index] contents. Since 
> port->iotype is not set to anything here, it defaults to
> '0', which happens to be UPIO_PORT.

Btw, we have a new constant to tell the 8250 core that IO type is not set,
but that one is not used here.

> The reason it doesn't immediately crash and burn is that
> this is still only setting up the structures for later
> use, but I assume that trying to use console=ttyS0, or
> opening /dev/ttyS0 on the uninitialized port would
> then cause an oops.
> 
> The bit I'm less sure about is why the
> serial8250_setup_port() function is called here in
> the first place. I assume it does something for
> /some/ architecture, but it's clearly wrong for
> most of them.
Arnd Bergmann Nov. 25, 2024, 9:53 a.m. UTC | #37
On Mon, Nov 25, 2024, at 08:55, Andy Shevchenko wrote:
> On Fri, Nov 22, 2024 at 08:24:37PM +0100, Arnd Bergmann wrote:
>> On Fri, Nov 22, 2024, at 18:22, Guenter Roeck wrote:
>> 
>> This looks like a bug in drivers/tty/serial/8250/8250_platform.c
>> to me, not something the architectures did wrong. My impression
>> from __serial8250_isa_init_ports() is that this is supposed
>> to initialize exactly the ports in the old_serial_port[]
>> array, which is filled only on alpha, m68k and x86 but not
>> on the other ones.
>
>> Andy moved this code in ffd8e8bd26e9 ("serial: 8250: Extract
>> platform driver"), but I don't think this move by itself
>> changed anything.
>
> Yep. the idea was to purely split this code out of the core
> library parts. I was not intending any functional changes.

Ok.

> I believe it's a preexisted bug, but if you can point out to
> the breakage I made, please do it, so I would be able to fix
> ASAP.
>
>> serial8250_setup_port() is where it ends up using the
>> uninitialized serial8250_ports[index] contents. Since 
>> port->iotype is not set to anything here, it defaults to
>> '0', which happens to be UPIO_PORT.
>
> Btw, we have a new constant to tell the 8250 core that IO type is not set,
> but that one is not used here.

So I see serial8250_setup_port() setting "up->cur_iotype = 0xFF"
by first calling serial8250_init_port(), this is the "not
set" value you mean, right?.

Right after that it calls serial8250_set_defaults(),
which sets "up->cur_iotype = p->iotype;", which may or
may not be initialized here.

The possible calls chains I see leading up to
serial8250_setup_port() are:

serial8250_register_8250_port(): here we first initialize
  the iotype incorrectly, then set uart->port.iotype and
  call serial8250_set_defaults() again to fix it.

module_init(serial8250_init): relies on the first
  serial8250_set_defaults() for the ISA ports since they
  are always UPIO_PORT, but sets the other ones (pnp, acpi,
  platform_data) correctly.

early_serial_setup(): called only on non-ISA platforms,
  shouldn't need to call serial8250_isa_init_ports() at
  all.

console_initcall(univ8250_console_init): Not completely
  sure here, it seems this now only allows ports that
  are registered from one of the methods above

Can you have a look at the patch below? I think this
correctly removes the broken serial8250_set_defaults()
while also still adding it in the one case that relies
on the implied version.

This does however revert f4c23a140d80 ("serial: 8250:
fix null-ptr-deref in serial8250_start_tx()") and
might bring back the bug came from opening an
uninitialized uart. On the other hand, I don't see
why that doesn't also crash from accessing the invalid
I/O port on the same architectures.

       Arnd

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 5f9f06911795..5b72309611cb 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -358,8 +358,6 @@ struct uart_8250_port *serial8250_setup_port(int index)
 
 	up->ops = &univ8250_driver_ops;
 
-	serial8250_set_defaults(up);
-
 	return up;
 }
 
diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c
index 66fd6d5d4835..d3c1668add58 100644
--- a/drivers/tty/serial/8250/8250_platform.c
+++ b/drivers/tty/serial/8250/8250_platform.c
@@ -94,6 +94,10 @@ static void __init __serial8250_isa_init_ports(void)
 		port->regshift = old_serial_port[i].iomem_reg_shift;
 
 		port->irqflags |= irqflag;
+
+		serial8250_set_defaults(port);
+
+		/* Allow Intel CE4100 and jailhouse to override defaults */
 		if (serial8250_isa_config != NULL)
 			serial8250_isa_config(i, &up->port, &up->capabilities);
 	}
Andy Shevchenko Nov. 25, 2024, 10:33 a.m. UTC | #38
On Mon, Nov 25, 2024 at 10:53:46AM +0100, Arnd Bergmann wrote:
> On Mon, Nov 25, 2024, at 08:55, Andy Shevchenko wrote:
> > On Fri, Nov 22, 2024 at 08:24:37PM +0100, Arnd Bergmann wrote:
> >> On Fri, Nov 22, 2024, at 18:22, Guenter Roeck wrote:
> >> 
> >> This looks like a bug in drivers/tty/serial/8250/8250_platform.c
> >> to me, not something the architectures did wrong. My impression
> >> from __serial8250_isa_init_ports() is that this is supposed
> >> to initialize exactly the ports in the old_serial_port[]
> >> array, which is filled only on alpha, m68k and x86 but not
> >> on the other ones.
> >
> >> Andy moved this code in ffd8e8bd26e9 ("serial: 8250: Extract
> >> platform driver"), but I don't think this move by itself
> >> changed anything.
> >
> > Yep. the idea was to purely split this code out of the core
> > library parts. I was not intending any functional changes.
> 
> Ok.
> 
> > I believe it's a preexisted bug, but if you can point out to
> > the breakage I made, please do it, so I would be able to fix
> > ASAP.
> >
> >> serial8250_setup_port() is where it ends up using the
> >> uninitialized serial8250_ports[index] contents. Since 
> >> port->iotype is not set to anything here, it defaults to
> >> '0', which happens to be UPIO_PORT.
> >
> > Btw, we have a new constant to tell the 8250 core that IO type is not set,
> > but that one is not used here.
> 
> So I see serial8250_setup_port() setting "up->cur_iotype = 0xFF"
> by first calling serial8250_init_port(), this is the "not
> set" value you mean, right?.

Yes, and we have a constant for that, I'll send a patch to make it clear.

> Right after that it calls serial8250_set_defaults(),
> which sets "up->cur_iotype = p->iotype;", which may or
> may not be initialized here.
> 
> The possible calls chains I see leading up to
> serial8250_setup_port() are:
> 
> serial8250_register_8250_port(): here we first initialize
>   the iotype incorrectly, then set uart->port.iotype and
>   call serial8250_set_defaults() again to fix it.
> 
> module_init(serial8250_init): relies on the first
>   serial8250_set_defaults() for the ISA ports since they
>   are always UPIO_PORT, but sets the other ones (pnp, acpi,
>   platform_data) correctly.
> 
> early_serial_setup(): called only on non-ISA platforms,
>   shouldn't need to call serial8250_isa_init_ports() at
>   all.
> 
> console_initcall(univ8250_console_init): Not completely
>   sure here, it seems this now only allows ports that
>   are registered from one of the methods above
> 
> Can you have a look at the patch below? I think this
> correctly removes the broken serial8250_set_defaults()
> while also still adding it in the one case that relies
> on the implied version.
> 
> This does however revert f4c23a140d80 ("serial: 8250:
> fix null-ptr-deref in serial8250_start_tx()") and
> might bring back the bug came from opening an
> uninitialized uart. On the other hand, I don't see
> why that doesn't also crash from accessing the invalid
> I/O port on the same architectures.

AFAICS it does only partial revert, so I don't see how your patch
may break that.

> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
> index 5f9f06911795..5b72309611cb 100644
> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -358,8 +358,6 @@ struct uart_8250_port *serial8250_setup_port(int index)
>  
>  	up->ops = &univ8250_driver_ops;
>  
> -	serial8250_set_defaults(up);
> -
>  	return up;
>  }
>  
> diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c
> index 66fd6d5d4835..d3c1668add58 100644
> --- a/drivers/tty/serial/8250/8250_platform.c
> +++ b/drivers/tty/serial/8250/8250_platform.c
> @@ -94,6 +94,10 @@ static void __init __serial8250_isa_init_ports(void)
>  		port->regshift = old_serial_port[i].iomem_reg_shift;
>  
>  		port->irqflags |= irqflag;
> +
> +		serial8250_set_defaults(port);
> +
> +		/* Allow Intel CE4100 and jailhouse to override defaults */
>  		if (serial8250_isa_config != NULL)
>  			serial8250_isa_config(i, &up->port, &up->capabilities);
>  	}
Arnd Bergmann Nov. 25, 2024, 11:06 a.m. UTC | #39
On Mon, Nov 25, 2024, at 11:33, Andy Shevchenko wrote:
> On Mon, Nov 25, 2024 at 10:53:46AM +0100, Arnd Bergmann wrote:
>> 
>> This does however revert f4c23a140d80 ("serial: 8250:
>> fix null-ptr-deref in serial8250_start_tx()") and
>> might bring back the bug came from opening an
>> uninitialized uart. On the other hand, I don't see
>> why that doesn't also crash from accessing the invalid
>> I/O port on the same architectures.
>
> AFAICS it does only partial revert, so I don't see how your patch
> may break that.

I think it's a complete revert, it's just not entirely obvious
since serial8250_setup_port() got split out by 9d86719f8769
("serial: 8250: Allow using ports higher than
SERIAL_8250_RUNTIME_UARTS") and the original callsite got
moved by your ffd8e8bd26e9 ("serial: 8250: Extract platform
driver").

What I suspect is going on with the f4c23a140d80 commit
is the same bug I mentioned earlier in this thread, where
__serial8250_isa_init_ports() just always registers
'nr_uarts' (CONFIG_SERIAL_8250_RUNTIME_UARTS) ports,
unlike any other serial driver.

This used to be required before 9d86719f8769 ("serial:
8250: Allow using ports higher than SERIAL_8250_RUNTIME_UARTS"),
but I don't see why this is still a thing now, other than
for using setserial on i486-class PCs with nonstandard ISA
ports.

On non-x86 machines, it only ever seems to create extra
ports that are likely to crash the system if opened, either
because they lack proper serial_in/serial_out callbacks,
or because the default UPIO_PORT callbacks end up poking
unmapped memory.

Do you see any reason why we can't just do the version below?

        Arnd

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 5f9f06911795..5b72309611cb 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -358,8 +358,6 @@ struct uart_8250_port *serial8250_setup_port(int index)
 
 	up->ops = &univ8250_driver_ops;
 
-	serial8250_set_defaults(up);
-
 	return up;
 }
 
diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c
index 66fd6d5d4835..7675536b6396 100644
--- a/drivers/tty/serial/8250/8250_platform.c
+++ b/drivers/tty/serial/8250/8250_platform.c
@@ -33,8 +33,6 @@
 unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 unsigned int skip_txen_test;
 
-unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
-
 #include <asm/serial.h>
 
 /*
@@ -50,6 +48,8 @@ static const struct old_serial_port old_serial_port[] = {
 	SERIAL_PORT_DFNS /* defined in asm/serial.h */
 };
 
+unsigned int nr_uarts = ARRAY_SIZE(old_serial_port);;
+
 serial8250_isa_config_fn serial8250_isa_config;
 void serial8250_set_isa_configurator(serial8250_isa_config_fn v)
 {
@@ -65,9 +65,9 @@ static void __init __serial8250_isa_init_ports(void)
 		nr_uarts = UART_NR;
 
 	/*
-	 * Set up initial ISA ports based on nr_uart module param, or else
-	 * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not
-	 * need to increase nr_uarts when setting up the initial ISA ports.
+	 * Set up initial ISA ports based on nr_uart module param. Note that 
+	 * we do not need to increase nr_uarts when setting up the initial
+	 * ISA ports.
 	 */
 	for (i = 0; i < nr_uarts; i++)
 		serial8250_setup_port(i);
@@ -94,6 +94,10 @@ static void __init __serial8250_isa_init_ports(void)
 		port->regshift = old_serial_port[i].iomem_reg_shift;
 
 		port->irqflags |= irqflag;
+
+		serial8250_set_defaults(up);
+
+		/* Allow Intel CE4100 and jailhouse to override defaults */
 		if (serial8250_isa_config != NULL)
 			serial8250_isa_config(i, &up->port, &up->capabilities);
 	}
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 5b1cc009b977..3bf27cecf82e 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -198,17 +198,6 @@ config SERIAL_8250_NR_UARTS
 	  PCI enumeration and any ports that may be added at run-time
 	  via hot-plug, or any ISA multi-port serial cards.
 
-config SERIAL_8250_RUNTIME_UARTS
-	int "Number of 8250/16550 serial ports to register at runtime"
-	depends on SERIAL_8250
-	range 0 SERIAL_8250_NR_UARTS
-	default "4"
-	help
-	  Set this to the maximum number of serial ports you want
-	  the kernel to register at boot time.  This can be overridden
-	  with the module parameter "nr_uarts", or boot-time parameter
-	  8250.nr_uarts
-
 config SERIAL_8250_EXTENDED
 	bool "Extended 8250/16550 serial driver options"
 	depends on SERIAL_8250
Andy Shevchenko Nov. 25, 2024, 11:26 a.m. UTC | #40
On Mon, Nov 25, 2024 at 12:06:16PM +0100, Arnd Bergmann wrote:
> On Mon, Nov 25, 2024, at 11:33, Andy Shevchenko wrote:
> > On Mon, Nov 25, 2024 at 10:53:46AM +0100, Arnd Bergmann wrote:
> >> 
> >> This does however revert f4c23a140d80 ("serial: 8250:
> >> fix null-ptr-deref in serial8250_start_tx()") and
> >> might bring back the bug came from opening an
> >> uninitialized uart. On the other hand, I don't see
> >> why that doesn't also crash from accessing the invalid
> >> I/O port on the same architectures.
> >
> > AFAICS it does only partial revert, so I don't see how your patch
> > may break that.
> 
> I think it's a complete revert, it's just not entirely obvious
> since serial8250_setup_port() got split out by 9d86719f8769
> ("serial: 8250: Allow using ports higher than
> SERIAL_8250_RUNTIME_UARTS") and the original callsite got
> moved by your ffd8e8bd26e9 ("serial: 8250: Extract platform
> driver").

Ah, okay.

> What I suspect is going on with the f4c23a140d80 commit
> is the same bug I mentioned earlier in this thread, where
> __serial8250_isa_init_ports() just always registers
> 'nr_uarts' (CONFIG_SERIAL_8250_RUNTIME_UARTS) ports,
> unlike any other serial driver.

But the configuration can give less than old_serial_port contains.
See dozens of the explicit settings in the defconfigs.

> This used to be required before 9d86719f8769 ("serial:
> 8250: Allow using ports higher than SERIAL_8250_RUNTIME_UARTS"),
> but I don't see why this is still a thing now, other than
> for using setserial on i486-class PCs with nonstandard ISA
> ports.
> 
> On non-x86 machines, it only ever seems to create extra
> ports that are likely to crash the system if opened, either
> because they lack proper serial_in/serial_out callbacks,
> or because the default UPIO_PORT callbacks end up poking
> unmapped memory.
> 
> Do you see any reason why we can't just do the version below?

Perhaps we may do this way (it seems better to me than previous suggestions),
but it also needs to be carefully checked against those configurations that set
it explicitly.

...

If we go this way, it would be also nice to add a comment explaining that
this is module parameter (as it's done for those ones above).

> +unsigned int nr_uarts = ARRAY_SIZE(old_serial_port);;

...

>  	/*
> -	 * Set up initial ISA ports based on nr_uart module param, or else
> -	 * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not
> -	 * need to increase nr_uarts when setting up the initial ISA ports.

> +	 * Set up initial ISA ports based on nr_uart module param. Note that 

Just in case it has a trailing whitespace.

> +	 * we do not need to increase nr_uarts when setting up the initial
> +	 * ISA ports.
>  	 */
Arnd Bergmann Nov. 25, 2024, 1:50 p.m. UTC | #41
On Mon, Nov 25, 2024, at 12:26, Andy Shevchenko wrote:
> On Mon, Nov 25, 2024 at 12:06:16PM +0100, Arnd Bergmann wrote:
>> What I suspect is going on with the f4c23a140d80 commit
>> is the same bug I mentioned earlier in this thread, where
>> __serial8250_isa_init_ports() just always registers
>> 'nr_uarts' (CONFIG_SERIAL_8250_RUNTIME_UARTS) ports,
>> unlike any other serial driver.
>
> But the configuration can give less than old_serial_port contains.
> See dozens of the explicit settings in the defconfigs.

I don't see any of the upstream defconfigs doing this
though, the only ones setting CONFIG_SERIAL_8250_RUNTIME_UARTS
are those that have an empty old_serial_port[]. 

Note that SERIAL_PORT_DFNS is only defined on x86, alpha
and m68k (for q40), which are the main PC-like platforms.
I see that all three have identical definitions of
SERIAL_PORT_DFNS, so I think these should just be moved
next to the __serial8250_isa_init_ports definition, with
the entire thing moved into a separate ISA driver or
an #ifdef around it. This is of course not the problem
at hand, but it would help separate the x86/isa and
non-x86 platform device cases further.

>> This used to be required before 9d86719f8769 ("serial:
>> 8250: Allow using ports higher than SERIAL_8250_RUNTIME_UARTS"),
>> but I don't see why this is still a thing now, other than
>> for using setserial on i486-class PCs with nonstandard ISA
>> ports.
>> 
>> On non-x86 machines, it only ever seems to create extra
>> ports that are likely to crash the system if opened, either
>> because they lack proper serial_in/serial_out callbacks,
>> or because the default UPIO_PORT callbacks end up poking
>> unmapped memory.
>> 
>> Do you see any reason why we can't just do the version below?
>
> Perhaps we may do this way (it seems better to me than previous 
> suggestions), but it also needs to be carefully checked against
> those configurations that set it explicitly.

Yes, at least to make sure that the numbering of the uarts
does not change. I expect it's actually the same, but don't
know for sure.

> If we go this way, it would be also nice to add a comment explaining that
> this is module parameter (as it's done for those ones above).
>
>> +unsigned int nr_uarts = ARRAY_SIZE(old_serial_port);;

Right.

     Arnd
Andy Shevchenko Nov. 25, 2024, 3:42 p.m. UTC | #42
On Mon, Nov 25, 2024 at 02:50:56PM +0100, Arnd Bergmann wrote:
> On Mon, Nov 25, 2024, at 12:26, Andy Shevchenko wrote:
> > On Mon, Nov 25, 2024 at 12:06:16PM +0100, Arnd Bergmann wrote:

...

> >> What I suspect is going on with the f4c23a140d80 commit
> >> is the same bug I mentioned earlier in this thread, where
> >> __serial8250_isa_init_ports() just always registers
> >> 'nr_uarts' (CONFIG_SERIAL_8250_RUNTIME_UARTS) ports,
> >> unlike any other serial driver.
> >
> > But the configuration can give less than old_serial_port contains.
> > See dozens of the explicit settings in the defconfigs.
> 
> I don't see any of the upstream defconfigs doing this
> though, the only ones setting CONFIG_SERIAL_8250_RUNTIME_UARTS
> are those that have an empty old_serial_port[]. 

A-ha, a good catch. I haven't checked the actual contents of the
old_serial_port for those configurations.

> Note that SERIAL_PORT_DFNS is only defined on x86, alpha
> and m68k (for q40), which are the main PC-like platforms.
> I see that all three have identical definitions of
> SERIAL_PORT_DFNS, so I think these should just be moved
> next to the __serial8250_isa_init_ports definition, with
> the entire thing moved into a separate ISA driver or
> an #ifdef around it. This is of course not the problem
> at hand, but it would help separate the x86/isa and
> non-x86 platform device cases further.

It's nice idea, but yes, we can think about it later.

> >> This used to be required before 9d86719f8769 ("serial:
> >> 8250: Allow using ports higher than SERIAL_8250_RUNTIME_UARTS"),
> >> but I don't see why this is still a thing now, other than
> >> for using setserial on i486-class PCs with nonstandard ISA
> >> ports.
> >> 
> >> On non-x86 machines, it only ever seems to create extra
> >> ports that are likely to crash the system if opened, either
> >> because they lack proper serial_in/serial_out callbacks,
> >> or because the default UPIO_PORT callbacks end up poking
> >> unmapped memory.
> >> 
> >> Do you see any reason why we can't just do the version below?
> >
> > Perhaps we may do this way (it seems better to me than previous 
> > suggestions), but it also needs to be carefully checked against
> > those configurations that set it explicitly.
> 
> Yes, at least to make sure that the numbering of the uarts
> does not change. I expect it's actually the same, but don't
> know for sure.

Me neither. And the issue with NULL pointer dereference needs to be retested.
Arnd Bergmann Nov. 25, 2024, 3:59 p.m. UTC | #43
On Mon, Nov 25, 2024, at 12:06, Arnd Bergmann wrote:
> +unsigned int nr_uarts = ARRAY_SIZE(old_serial_port);;
> +

Unfortunately, this breaks on non-x86 because of the check
added in 59cfc45f17d6 ("serial: 8250: Do nothing if nr_uarts=0").

I still think it's the right idea, but need to unwind further
to make this possible, and find a different fix for the bug
from that commit.

      Arnd
Maciej W. Rozycki Nov. 25, 2024, 4:54 p.m. UTC | #44
On Mon, 25 Nov 2024, Arnd Bergmann wrote:

> > But the configuration can give less than old_serial_port contains.
> > See dozens of the explicit settings in the defconfigs.
> 
> I don't see any of the upstream defconfigs doing this
> though, the only ones setting CONFIG_SERIAL_8250_RUNTIME_UARTS
> are those that have an empty old_serial_port[]. 
> 
> Note that SERIAL_PORT_DFNS is only defined on x86, alpha
> and m68k (for q40), which are the main PC-like platforms.

 May I suggest to call `serial8250_isa_init_ports':

	if (IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_ALPHA) ||
	    IS_ENABLED(CONFIG_M68K) || IS_ENABLED(CONFIG_X86))

then (or have an equivalent `select' in the relevant Kconfig files)?

 The whole point of this legacy setup is to poke at ISA serial ports that 
have been wired by jumpers or similar means (sometimes just hardwired) to 
their designated ISA port I/O locations.  Sometimes it means LPC rather 
than real ISA, but LPC stuff should mostly be covered by platform bindings 
rather than just blind poking, which may only be needed for platforms that 
have some kind of a generic config and no DT or other way (such as ACPI or 
ISA PNP) to discover actual ports.

> I see that all three have identical definitions of
> SERIAL_PORT_DFNS, so I think these should just be moved
> next to the __serial8250_isa_init_ports definition, with
> the entire thing moved into a separate ISA driver or
> an #ifdef around it. This is of course not the problem
> at hand, but it would help separate the x86/isa and
> non-x86 platform device cases further.

 This SERIAL_PORT_DFNS definition is just original ISA stuff, so it does 
apply universally across CONFIG_ISA platforms.  Original ISA option cards 
have had no way to discover other than by blind-poking (or giving port I/O 
locations by hand via a kernel parameter).

  Maciej
Arnd Bergmann Nov. 25, 2024, 5:54 p.m. UTC | #45
On Mon, Nov 25, 2024, at 17:54, Maciej W. Rozycki wrote:
> On Mon, 25 Nov 2024, Arnd Bergmann wrote:
>
>> > But the configuration can give less than old_serial_port contains.
>> > See dozens of the explicit settings in the defconfigs.
>> 
>> I don't see any of the upstream defconfigs doing this
>> though, the only ones setting CONFIG_SERIAL_8250_RUNTIME_UARTS
>> are those that have an empty old_serial_port[]. 
>> 
>> Note that SERIAL_PORT_DFNS is only defined on x86, alpha
>> and m68k (for q40), which are the main PC-like platforms.
>
>  May I suggest to call `serial8250_isa_init_ports':
>
> 	if (IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_ALPHA) ||
> 	    IS_ENABLED(CONFIG_M68K) || IS_ENABLED(CONFIG_X86))
>
> then (or have an equivalent `select' in the relevant Kconfig files)?

Right, I think that makes sense, but I'm a little worried
about renumbering or incorrectly configuring the uarts on
a non-x86 system that might have ISA slots and also register
a 8250 console.

E.g. on the RM200, two serial ports get registered on
MMIO addresses:

arch/mips/sni/rm200.c:static struct serial8250_platform_data rm200_data[] = {
arch/mips/sni/rm200.c-  MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4),
arch/mips/sni/rm200.c-  MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3),
arch/mips/sni/rm200.c-  { },
arch/mips/sni/rm200.c-};

so these would become ports ttyS4 and ttyS5 if the first four
ports get reserved for ISA cards, or disappear when using the
default CONFIG_SERIAL_8250_NR_UARTS=4.

      Arnd
Maciej W. Rozycki Nov. 25, 2024, 6:42 p.m. UTC | #46
On Mon, 25 Nov 2024, Arnd Bergmann wrote:

> >  May I suggest to call `serial8250_isa_init_ports':
> >
> > 	if (IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_ALPHA) ||
> > 	    IS_ENABLED(CONFIG_M68K) || IS_ENABLED(CONFIG_X86))
> >
> > then (or have an equivalent `select' in the relevant Kconfig files)?
> 
> Right, I think that makes sense, but I'm a little worried
> about renumbering or incorrectly configuring the uarts on
> a non-x86 system that might have ISA slots and also register
> a 8250 console.
> 
> E.g. on the RM200, two serial ports get registered on
> MMIO addresses:
> 
> arch/mips/sni/rm200.c:static struct serial8250_platform_data rm200_data[] = {
> arch/mips/sni/rm200.c-  MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4),
> arch/mips/sni/rm200.c-  MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3),
> arch/mips/sni/rm200.c-  { },
> arch/mips/sni/rm200.c-};
> 
> so these would become ports ttyS4 and ttyS5 if the first four
> ports get reserved for ISA cards, or disappear when using the
> default CONFIG_SERIAL_8250_NR_UARTS=4.

 I think it's still the correct thing to do or ISA card ports won't be 
accessible.  Then where there are platform ports, they ought to be given 
precedence so that they are given lower indices.  I'm undecided as to 
whether we actually need to get it sorted now or wait for someone with an 
RM200 system to trigger the problem (with the possible workarounds being:

1. Do not plug any ISA cards with serial ports.

2. Set CONFIG_SERIAL_8250_RUNTIME_UARTS or "8250.nr_uarts" to 0.

).  It might be quite easy to get sorted though, so I'd be leaning towards 
at least checking how feasible it would be.

  Maciej
Guenter Roeck Dec. 4, 2024, 6:51 p.m. UTC | #47
On 11/25/24 09:54, Arnd Bergmann wrote:
> On Mon, Nov 25, 2024, at 17:54, Maciej W. Rozycki wrote:
>> On Mon, 25 Nov 2024, Arnd Bergmann wrote:
>>
>>>> But the configuration can give less than old_serial_port contains.
>>>> See dozens of the explicit settings in the defconfigs.
>>>
>>> I don't see any of the upstream defconfigs doing this
>>> though, the only ones setting CONFIG_SERIAL_8250_RUNTIME_UARTS
>>> are those that have an empty old_serial_port[].
>>>
>>> Note that SERIAL_PORT_DFNS is only defined on x86, alpha
>>> and m68k (for q40), which are the main PC-like platforms.
>>
>>   May I suggest to call `serial8250_isa_init_ports':
>>
>> 	if (IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_ALPHA) ||
>> 	    IS_ENABLED(CONFIG_M68K) || IS_ENABLED(CONFIG_X86))
>>
>> then (or have an equivalent `select' in the relevant Kconfig files)?
> 
> Right, I think that makes sense, but I'm a little worried
> about renumbering or incorrectly configuring the uarts on
> a non-x86 system that might have ISA slots and also register
> a 8250 console.
> 
> E.g. on the RM200, two serial ports get registered on
> MMIO addresses:
> 
> arch/mips/sni/rm200.c:static struct serial8250_platform_data rm200_data[] = {
> arch/mips/sni/rm200.c-  MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4),
> arch/mips/sni/rm200.c-  MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3),
> arch/mips/sni/rm200.c-  { },
> arch/mips/sni/rm200.c-};
> 
> so these would become ports ttyS4 and ttyS5 if the first four
> ports get reserved for ISA cards, or disappear when using the
> default CONFIG_SERIAL_8250_NR_UARTS=4.
> 

Any progress ?

Guenter
Guenter Roeck Dec. 4, 2024, 9:09 p.m. UTC | #48
On Mon, Nov 25, 2024 at 04:59:00PM +0100, Arnd Bergmann wrote:
> On Mon, Nov 25, 2024, at 12:06, Arnd Bergmann wrote:
> > +unsigned int nr_uarts = ARRAY_SIZE(old_serial_port);;
> > +
> 
> Unfortunately, this breaks on non-x86 because of the check
> added in 59cfc45f17d6 ("serial: 8250: Do nothing if nr_uarts=0").
> 
> I still think it's the right idea, but need to unwind further
> to make this possible, and find a different fix for the bug
> from that commit.
> 

I decided to apply the patch below to my fixes branch. It doesn't change
the code, it just gets rid of the warning backtrace.

Guenter

---
Arnd Bergmann Dec. 4, 2024, 10:17 p.m. UTC | #49
On Wed, Dec 4, 2024, at 22:09, Guenter Roeck wrote:
> On Mon, Nov 25, 2024 at 04:59:00PM +0100, Arnd Bergmann wrote:
>> On Mon, Nov 25, 2024, at 12:06, Arnd Bergmann wrote:
>> > +unsigned int nr_uarts = ARRAY_SIZE(old_serial_port);;
>> > +
>> 
>> Unfortunately, this breaks on non-x86 because of the check
>> added in 59cfc45f17d6 ("serial: 8250: Do nothing if nr_uarts=0").
>> 
>> I still think it's the right idea, but need to unwind further
>> to make this possible, and find a different fix for the bug
>> from that commit.
>> 
>
> I decided to apply the patch below to my fixes branch. It doesn't change
> the code, it just gets rid of the warning backtrace.

I got stuck in this rabbit hole of running into more issues
with the 8250 driver. Any time you touch something, it breaks
elsewhere.

I've uploaded what I have here now:

https://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground.git/log/?h=8250-cleanup

but this probably needs more testing, and a few smaller changes

>  drivers/tty/serial/8250/8250_port.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/tty/serial/8250/8250_port.c 
> b/drivers/tty/serial/8250/8250_port.c
> index 4d63d80e78a9..649e74e9b52f 100644
> --- a/drivers/tty/serial/8250/8250_port.c
> +++ b/drivers/tty/serial/8250/8250_port.c
> @@ -467,7 +467,8 @@ static void set_io_from_upio(struct uart_port *p)
>  		break;
>  #endif
>  	default:
> -		WARN(1, "Unsupported UART type %x\n", p->iotype);
> +		WARN(p->iotype != UPIO_PORT || p->iobase,
> +		     "Unsupported UART type %x\n", p->iotype);
>  		p->serial_in = no_serial_in;
>  		p->serial_out = no_serial_out;
>  	}

I had considered this at first but didn't really want to do
it like this because we should not be registering empty ports
on platforms that don't use the setserial style override for
the port configuration.

It does of course address the warning, just not the
underlying bug.

      Arnd
Guenter Roeck Dec. 4, 2024, 10:44 p.m. UTC | #50
On 12/4/24 14:17, Arnd Bergmann wrote:
> On Wed, Dec 4, 2024, at 22:09, Guenter Roeck wrote:
>> On Mon, Nov 25, 2024 at 04:59:00PM +0100, Arnd Bergmann wrote:
>>> On Mon, Nov 25, 2024, at 12:06, Arnd Bergmann wrote:
>>>> +unsigned int nr_uarts = ARRAY_SIZE(old_serial_port);;
>>>> +
>>>
>>> Unfortunately, this breaks on non-x86 because of the check
>>> added in 59cfc45f17d6 ("serial: 8250: Do nothing if nr_uarts=0").
>>>
>>> I still think it's the right idea, but need to unwind further
>>> to make this possible, and find a different fix for the bug
>>> from that commit.
>>>
>>
>> I decided to apply the patch below to my fixes branch. It doesn't change
>> the code, it just gets rid of the warning backtrace.
> 
> I got stuck in this rabbit hole of running into more issues
> with the 8250 driver. Any time you touch something, it breaks
> elsewhere.
> 
> I've uploaded what I have here now:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground.git/log/?h=8250-cleanup
> 
> but this probably needs more testing, and a few smaller changes
> 
>>   drivers/tty/serial/8250/8250_port.c | 3 ++-
>>   1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/tty/serial/8250/8250_port.c
>> b/drivers/tty/serial/8250/8250_port.c
>> index 4d63d80e78a9..649e74e9b52f 100644
>> --- a/drivers/tty/serial/8250/8250_port.c
>> +++ b/drivers/tty/serial/8250/8250_port.c
>> @@ -467,7 +467,8 @@ static void set_io_from_upio(struct uart_port *p)
>>   		break;
>>   #endif
>>   	default:
>> -		WARN(1, "Unsupported UART type %x\n", p->iotype);
>> +		WARN(p->iotype != UPIO_PORT || p->iobase,
>> +		     "Unsupported UART type %x\n", p->iotype);
>>   		p->serial_in = no_serial_in;
>>   		p->serial_out = no_serial_out;
>>   	}
> 
> I had considered this at first but didn't really want to do
> it like this because we should not be registering empty ports
> on platforms that don't use the setserial style override for
> the port configuration.
> 
> It does of course address the warning, just not the
> underlying bug.
> 

I had a look myself, and concluded that a clean fix will likely require
substantial changes to avoid regressions. Your patch series pretty much
confirms that. This is why I came up with the hack below. Yes, it doesn't
get rid of the underlying problem, but IMO it is good enough for 6.13,
or at least not worse than 6.12, while at the same time avoiding the
warning backtrace. It seems to me that a clean fix would be 6.14 material,
and I really don't want those backtraces to clog up test logs until then.

Thanks,
Guenter
Arnd Bergmann Dec. 5, 2024, 7:08 a.m. UTC | #51
On Wed, Dec 4, 2024, at 23:44, Guenter Roeck wrote:
> On 12/4/24 14:17, Arnd Bergmann wrote:
>> On Wed, Dec 4, 2024, at 22:09, Guenter Roeck wrote:
>> 
>> I had considered this at first but didn't really want to do
>> it like this because we should not be registering empty ports
>> on platforms that don't use the setserial style override for
>> the port configuration.
>> 
>> It does of course address the warning, just not the
>> underlying bug.
>> 
>
> I had a look myself, and concluded that a clean fix will likely require
> substantial changes to avoid regressions. Your patch series pretty much
> confirms that. This is why I came up with the hack below. Yes, it doesn't
> get rid of the underlying problem, but IMO it is good enough for 6.13,
> or at least not worse than 6.12, while at the same time avoiding the
> warning backtrace. It seems to me that a clean fix would be 6.14 material,
> and I really don't want those backtraces to clog up test logs until then.

Fair enough, let's use your patch then for 6,13,

Acked-by: Arnd Bergmann <arnd@arndb.de>
Guenter Roeck Dec. 5, 2024, 2:31 p.m. UTC | #52
On 12/4/24 23:08, Arnd Bergmann wrote:
> On Wed, Dec 4, 2024, at 23:44, Guenter Roeck wrote:
>> On 12/4/24 14:17, Arnd Bergmann wrote:
>>> On Wed, Dec 4, 2024, at 22:09, Guenter Roeck wrote:
>>>
>>> I had considered this at first but didn't really want to do
>>> it like this because we should not be registering empty ports
>>> on platforms that don't use the setserial style override for
>>> the port configuration.
>>>
>>> It does of course address the warning, just not the
>>> underlying bug.
>>>
>>
>> I had a look myself, and concluded that a clean fix will likely require
>> substantial changes to avoid regressions. Your patch series pretty much
>> confirms that. This is why I came up with the hack below. Yes, it doesn't
>> get rid of the underlying problem, but IMO it is good enough for 6.13,
>> or at least not worse than 6.12, while at the same time avoiding the
>> warning backtrace. It seems to me that a clean fix would be 6.14 material,
>> and I really don't want those backtraces to clog up test logs until then.
> 
> Fair enough, let's use your patch then for 6,13,
> 
> Acked-by: Arnd Bergmann <arnd@arndb.de>


Ok, I just sent it out as real patch with your Ack applied.

Thanks,
Guenter
Andy Shevchenko Dec. 6, 2024, 3:44 p.m. UTC | #53
On Wed, Dec 04, 2024 at 11:17:56PM +0100, Arnd Bergmann wrote:
> On Wed, Dec 4, 2024, at 22:09, Guenter Roeck wrote:

...

> I got stuck in this rabbit hole of running into more issues
> with the 8250 driver. Any time you touch something, it breaks
> elsewhere.
> 
> I've uploaded what I have here now:

FWIW, I have briefly looked at it, some patches I appreciate very much, some of
them I think need more testing and one thing I don't really like is putting
code back to 8250_core. Thinking about that, perhaps we need to restore 8250.c
(or alike) for collecting non-library / non-_particular_-driver-leaf?

> https://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground.git/log/?h=8250-cleanup
> 
> but this probably needs more testing, and a few smaller changes

Definitely!
Arnd Bergmann Dec. 6, 2024, 4:02 p.m. UTC | #54
On Fri, Dec 6, 2024, at 16:44, Andy Shevchenko wrote:
> On Wed, Dec 04, 2024 at 11:17:56PM +0100, Arnd Bergmann wrote:
>> On Wed, Dec 4, 2024, at 22:09, Guenter Roeck wrote:
>
>> I got stuck in this rabbit hole of running into more issues
>> with the 8250 driver. Any time you touch something, it breaks
>> elsewhere.
>> 
>> I've uploaded what I have here now:
>
> FWIW, I have briefly looked at it, some patches I appreciate very much, some of
> them I think need more testing and one thing I don't really like is putting
> code back to 8250_core. Thinking about that, perhaps we need to restore 8250.c
> (or alike) for collecting non-library / non-_particular_-driver-leaf?

Thanks for taking a look!

I thought you might have different ideas on where some of the
code needs to go,  as you were the one who moved it out
previously.

However, I think the two bits that I ended up moving back
really belong in the core:

- serial8250_setup_ports() is needed to get the
  serial8250_ports[] into a sane state. Ideally this
  array would just be statically initialized, but I don't
  think we can actually express that in C code.

- serial8250_init()/serial8250_exit() are required for
  registering the driver itself (serial8250_reg) Having
  that part in the ISA driver made no sense to me.

The bit that is definitely ugly is how serial8250_isa_init()
gets called from serial8250_init() and univ8250_console_init(),
and I would prefer to remove that, but any attempt to do
that made it worse.

The problem here is that we can't just rely on link order:
For the console initcall and  serial8250_isa_init_ports(),
we have to call this before the core driver initialization,
but the serial8250-isa platform device has to be registered
after the core driver does. If we link both files into
a loadable module, there is an additional problem of
not being able to have more than one module_init() call.

      Arnd
diff mbox series

Patch

diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index a45d423ad10f..63a494d36a1f 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -220,7 +220,7 @@  config MOXA_INTELLIO
 
 config MOXA_SMARTIO
 	tristate "Moxa SmartIO support v. 2.0"
-	depends on SERIAL_NONSTANDARD && PCI
+	depends on SERIAL_NONSTANDARD && PCI && HAS_IOPORT
 	help
 	  Say Y here if you have a Moxa SmartIO multiport serial card and/or
 	  want to help develop a new version of this driver.
@@ -302,7 +302,7 @@  config GOLDFISH_TTY_EARLY_CONSOLE
 
 config IPWIRELESS
 	tristate "IPWireless 3G UMTS PCMCIA card support"
-	depends on PCMCIA && NETDEVICES
+	depends on PCMCIA && NETDEVICES && HAS_IOPORT
 	select PPP
 	help
 	  This is a driver for 3G UMTS PCMCIA card from IPWireless company. In
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index e3f482fd3de4..8f9aec2e7c7d 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -46,8 +46,10 @@  static unsigned int serial8250_early_in(struct uart_port *port, int offset)
 		return readl(port->membase + offset);
 	case UPIO_MEM32BE:
 		return ioread32be(port->membase + offset);
+#ifdef CONFIG_HAS_IOPORT
 	case UPIO_PORT:
 		return inb(port->iobase + offset);
+#endif
 	default:
 		return 0;
 	}
@@ -70,9 +72,11 @@  static void serial8250_early_out(struct uart_port *port, int offset, int value)
 	case UPIO_MEM32BE:
 		iowrite32be(value, port->membase + offset);
 		break;
+#ifdef CONFIG_HAS_IOPORT
 	case UPIO_PORT:
 		outb(value, port->iobase + offset);
 		break;
+#endif
 	}
 }
 
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 0d35c77fad9e..38ac5236d2ea 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -928,6 +928,7 @@  static int pci_netmos_init(struct pci_dev *dev)
 	return num_serial;
 }
 
+#ifdef CONFIG_HAS_IOPORT
 /*
  * These chips are available with optionally one parallel port and up to
  * two serial ports. Unfortunately they all have the same product id.
@@ -1054,6 +1055,7 @@  static void pci_ite887x_exit(struct pci_dev *dev)
 	ioport &= 0xffff;
 	release_region(ioport, ITE_887x_IOSIZE);
 }
+#endif /* CONFIG_HAS_IOPORT */
 
 /*
  * Oxford Semiconductor Inc.
@@ -1328,6 +1330,7 @@  static int pci_oxsemi_tornado_setup(struct serial_private *priv,
 #define QOPR_CLOCK_X8		0x0003
 #define QOPR_CLOCK_RATE_MASK	0x0003
 
+#ifdef CONFIG_HAS_IOPORT
 /* Quatech devices have their own extra interface features */
 static struct pci_device_id quatech_cards[] = {
 	{ PCI_DEVICE_DATA(QUATECH, QSC100,   1) },
@@ -1547,6 +1550,7 @@  static int pci_quatech_setup(struct serial_private *priv,
 		pci_warn(priv->dev, "software control of RS422 features not currently supported.\n");
 	return pci_default_setup(priv, board, port, idx);
 }
+#endif /* CONFIG_HAS_IOPORT */
 
 static int pci_default_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
@@ -1826,6 +1830,7 @@  static int skip_tx_en_setup(struct serial_private *priv,
 	return pci_default_setup(priv, board, port, idx);
 }
 
+#ifdef CONFIG_HAS_IOPORT
 static void kt_handle_break(struct uart_port *p)
 {
 	struct uart_8250_port *up = up_to_u8250p(p);
@@ -1869,6 +1874,7 @@  static int kt_serial_setup(struct serial_private *priv,
 	port->port.handle_break = kt_handle_break;
 	return skip_tx_en_setup(priv, board, port, idx);
 }
+#endif /* CONFIG_HAS_IOPORT */
 
 static int pci_eg20t_init(struct pci_dev *dev)
 {
@@ -1913,6 +1919,7 @@  pci_wch_ch38x_setup(struct serial_private *priv,
 #define CH384_XINT_ENABLE_REG   0xEB
 #define CH384_XINT_ENABLE_BIT   0x02
 
+#ifdef CONFIG_HAS_IOPORT
 static int pci_wch_ch38x_init(struct pci_dev *dev)
 {
 	int max_port;
@@ -1940,6 +1947,7 @@  static void pci_wch_ch38x_exit(struct pci_dev *dev)
 	iobase = pci_resource_start(dev, 0);
 	outb(0x0, iobase + CH384_XINT_ENABLE_REG);
 }
+#endif /* CONFIG_HAS_IOPORT */
 
 
 static int
@@ -2171,6 +2179,7 @@  static struct pci_serial_quirk pci_serial_quirks[] = {
 		.subdevice	= PCI_ANY_ID,
 		.setup		= ce4100_serial_setup,
 	},
+#ifdef CONFIG_HAS_IOPORT
 	{
 		.vendor		= PCI_VENDOR_ID_INTEL,
 		.device		= PCI_DEVICE_ID_INTEL_PATSBURG_KT,
@@ -2190,6 +2199,7 @@  static struct pci_serial_quirk pci_serial_quirks[] = {
 		.setup		= pci_default_setup,
 		.exit		= pci_ite887x_exit,
 	},
+#endif
 	/*
 	 * National Instruments
 	 */
@@ -2311,6 +2321,7 @@  static struct pci_serial_quirk pci_serial_quirks[] = {
 		.exit		= pci_ni8430_exit,
 	},
 	/* Quatech */
+#ifdef CONFIG_HAS_IOPORT
 	{
 		.vendor		= PCI_VENDOR_ID_QUATECH,
 		.device		= PCI_ANY_ID,
@@ -2319,6 +2330,7 @@  static struct pci_serial_quirk pci_serial_quirks[] = {
 		.init		= pci_quatech_init,
 		.setup		= pci_quatech_setup,
 	},
+#endif
 	/*
 	 * Panacom
 	 */
@@ -2836,6 +2848,7 @@  static struct pci_serial_quirk pci_serial_quirks[] = {
 		.subdevice      = PCI_ANY_ID,
 		.setup          = pci_wch_ch38x_setup,
 	},
+#ifdef CONFIG_HAS_IOPORT
 	/* WCH CH384 8S card (16850 clone) */
 	{
 		.vendor         = PCIE_VENDOR_ID_WCH,
@@ -2846,6 +2859,7 @@  static struct pci_serial_quirk pci_serial_quirks[] = {
 		.exit		= pci_wch_ch38x_exit,
 		.setup          = pci_wch_ch38x_setup,
 	},
+#endif
 	/*
 	 * Broadcom TruManage (NetXtreme)
 	 */
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index fc9dd5d45295..1c5e39c1a469 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -338,6 +338,7 @@  static void default_serial_dl_write(struct uart_8250_port *up, u32 value)
 	serial_out(up, UART_DLM, value >> 8 & 0xff);
 }
 
+#ifdef CONFIG_HAS_IOPORT
 static unsigned int hub6_serial_in(struct uart_port *p, int offset)
 {
 	offset = offset << p->regshift;
@@ -351,6 +352,7 @@  static void hub6_serial_out(struct uart_port *p, int offset, int value)
 	outb(p->hub6 - 1 + offset, p->iobase);
 	outb(value, p->iobase + 1);
 }
+#endif /* CONFIG_HAS_IOPORT */
 
 static unsigned int mem_serial_in(struct uart_port *p, int offset)
 {
@@ -400,6 +402,7 @@  static unsigned int mem32be_serial_in(struct uart_port *p, int offset)
 	return ioread32be(p->membase + offset);
 }
 
+#ifdef CONFIG_HAS_IOPORT
 static unsigned int io_serial_in(struct uart_port *p, int offset)
 {
 	offset = offset << p->regshift;
@@ -411,6 +414,24 @@  static void io_serial_out(struct uart_port *p, int offset, int value)
 	offset = offset << p->regshift;
 	outb(value, p->iobase + offset);
 }
+#endif
+static unsigned int no_serial_in(struct uart_port *p, int offset)
+{
+	return (unsigned int)-1;
+}
+
+static void no_serial_out(struct uart_port *p, int offset, int value)
+{
+}
+
+#ifdef CONFIG_HAS_IOPORT
+static inline bool is_upf_fourport(struct uart_port *port)
+{
+	return port->flags & UPF_FOURPORT;
+}
+#else
+#define is_upf_fourport(x)	false
+#endif
 
 static int serial8250_default_handle_irq(struct uart_port *port);
 
@@ -422,10 +443,12 @@  static void set_io_from_upio(struct uart_port *p)
 	up->dl_write = default_serial_dl_write;
 
 	switch (p->iotype) {
+#ifdef CONFIG_HAS_IOPORT
 	case UPIO_HUB6:
 		p->serial_in = hub6_serial_in;
 		p->serial_out = hub6_serial_out;
 		break;
+#endif
 
 	case UPIO_MEM:
 		p->serial_in = mem_serial_in;
@@ -446,11 +469,16 @@  static void set_io_from_upio(struct uart_port *p)
 		p->serial_in = mem32be_serial_in;
 		p->serial_out = mem32be_serial_out;
 		break;
-
-	default:
+#ifdef CONFIG_HAS_IOPORT
+	case UPIO_PORT:
 		p->serial_in = io_serial_in;
 		p->serial_out = io_serial_out;
 		break;
+#endif
+	default:
+		WARN(1, "Unsupported UART type %x\n", p->iotype);
+		p->serial_in = no_serial_in;
+		p->serial_out = no_serial_out;
 	}
 	/* Remember loaded iotype */
 	up->cur_iotype = p->iotype;
@@ -1318,7 +1346,7 @@  static void autoconfig_irq(struct uart_8250_port *up)
 	unsigned long irqs;
 	int irq;
 
-	if (port->flags & UPF_FOURPORT) {
+	if (is_upf_fourport(port)) {
 		ICP = (port->iobase & 0xfe0) | 0x1f;
 		save_ICP = inb_p(ICP);
 		outb_p(0x80, ICP);
@@ -1337,7 +1365,7 @@  static void autoconfig_irq(struct uart_8250_port *up)
 	irqs = probe_irq_on();
 	serial8250_out_MCR(up, 0);
 	udelay(10);
-	if (port->flags & UPF_FOURPORT) {
+	if (is_upf_fourport(port)) {
 		serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS);
 	} else {
 		serial8250_out_MCR(up,
@@ -1361,7 +1389,7 @@  static void autoconfig_irq(struct uart_8250_port *up)
 	serial_out(up, UART_IER, save_ier);
 	uart_port_unlock_irq(port);
 
-	if (port->flags & UPF_FOURPORT)
+	if (is_upf_fourport(port))
 		outb_p(save_ICP, ICP);
 
 	port->irq = (irq > 0) ? irq : 0;
@@ -2438,7 +2466,7 @@  int serial8250_do_startup(struct uart_port *port)
 	 */
 	up->ier = UART_IER_RLSI | UART_IER_RDI;
 
-	if (port->flags & UPF_FOURPORT) {
+	if (is_upf_fourport(port)) {
 		unsigned int icp;
 		/*
 		 * Enable interrupts on the AST Fourport board
@@ -2483,7 +2511,7 @@  void serial8250_do_shutdown(struct uart_port *port)
 		serial8250_release_dma(up);
 
 	uart_port_lock_irqsave(port, &flags);
-	if (port->flags & UPF_FOURPORT) {
+	if (is_upf_fourport(port)) {
 		/* reset interrupts on the AST Fourport board */
 		inb((port->iobase & 0xfe0) | 0x1f);
 		port->mctrl |= TIOCM_OUT1;
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 47ff50763c04..54bf98869abf 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -6,7 +6,6 @@ 
 
 config SERIAL_8250
 	tristate "8250/16550 and compatible serial support"
-	depends on !S390
 	select SERIAL_CORE
 	select SERIAL_MCTRL_GPIO if GPIOLIB
 	help
@@ -72,7 +71,7 @@  config SERIAL_8250_16550A_VARIANTS
 
 config SERIAL_8250_FINTEK
 	bool "Support for Fintek variants"
-	depends on SERIAL_8250
+	depends on SERIAL_8250 && HAS_IOPORT
 	help
 	  Selecting this option will add support for the RS232 and RS485
 	  capabilities of the Fintek F81216A LPC to 4 UART as well similar
@@ -136,7 +135,7 @@  config SERIAL_8250_PCILIB
 
 config SERIAL_8250_PCI
 	tristate "8250/16550 PCI device support"
-	depends on SERIAL_8250 && PCI
+	depends on SERIAL_8250 && PCI && HAS_IOPORT
 	select SERIAL_8250_PCILIB
 	default SERIAL_8250
 	help
@@ -163,7 +162,7 @@  config SERIAL_8250_HP300
 
 config SERIAL_8250_CS
 	tristate "8250/16550 PCMCIA device support"
-	depends on PCMCIA && SERIAL_8250
+	depends on PCMCIA && SERIAL_8250 && HAS_IOPORT
 	help
 	  Say Y here to enable support for 16-bit PCMCIA serial devices,
 	  including serial port cards, modems, and the modem functions of
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index ffcf4882b25f..92c85e805c2a 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -874,7 +874,7 @@  config SERIAL_TXX9_STDSERIAL
 
 config SERIAL_JSM
 	tristate "Digi International NEO and Classic PCI Support"
-	depends on PCI
+	depends on PCI && HAS_IOPORT
 	select SERIAL_CORE
 	help
 	  This is a driver for Digi International's Neo and Classic series