diff mbox series

[v2,2/3] acpi: allow building without CONFIG_HAS_IOPORT

Message ID 20241011061948.3211423-2-arnd@kernel.org (mailing list archive)
State New
Headers show
Series [v2,1/3] acpi: make EC support compile-time conditional | expand

Commit Message

Arnd Bergmann Oct. 11, 2024, 6:18 a.m. UTC
From: Arnd Bergmann <arnd@arndb.de>

CONFIG_HAS_IOPORT will soon become optional and cause a build time
failure when it is disabled but a driver calls inb()/outb(). At the
moment, all architectures that can support ACPI have port I/O, but this
is not necessarily the case in the future on non-x86 architectures.
The result is a set of errors like:

drivers/acpi/osl.c: In function 'acpi_os_read_port':
include/asm-generic/io.h:542:14: error: call to '_inb' declared with attribute error: inb()) requires CONFIG_HAS_IOPORT

Nothing should actually call these functions in this configuration,
and if it does, the result would be undefined behavior today, possibly
a NULL pointer dereference.

Change the low-level functions to return a proper error code when
HAS_IOPORT is disabled.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 drivers/acpi/cppc_acpi.c | 6 ++++--
 drivers/acpi/osl.c       | 8 ++++++++
 2 files changed, 12 insertions(+), 2 deletions(-)

Comments

Andy Shevchenko Oct. 11, 2024, 9:53 a.m. UTC | #1
On Fri, Oct 11, 2024 at 06:18:18AM +0000, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> 
> CONFIG_HAS_IOPORT will soon become optional and cause a build time
> failure when it is disabled but a driver calls inb()/outb(). At the
> moment, all architectures that can support ACPI have port I/O, but this
> is not necessarily the case in the future on non-x86 architectures.
> The result is a set of errors like:
> 
> drivers/acpi/osl.c: In function 'acpi_os_read_port':
> include/asm-generic/io.h:542:14: error: call to '_inb' declared with attribute error: inb()) requires CONFIG_HAS_IOPORT
> 
> Nothing should actually call these functions in this configuration,
> and if it does, the result would be undefined behavior today, possibly
> a NULL pointer dereference.
> 
> Change the low-level functions to return a proper error code when
> HAS_IOPORT is disabled.

...

> +	if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
> +		*value = BIT_MASK(width);
> +		return AE_NOT_IMPLEMENTED;

Perhaps it has already been discussed, but why do we need to file value with
semi-garbage when we know it's invalid anyway?

> +	}
Arnd Bergmann Oct. 11, 2024, 9:59 a.m. UTC | #2
On Fri, Oct 11, 2024, at 09:53, Andy Shevchenko wrote:
> On Fri, Oct 11, 2024 at 06:18:18AM +0000, Arnd Bergmann wrote:
>
> ...
>
>> +	if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
>> +		*value = BIT_MASK(width);
>> +		return AE_NOT_IMPLEMENTED;
>
> Perhaps it has already been discussed, but why do we need to file value with
> semi-garbage when we know it's invalid anyway?

It's not strictly necessary, just precaution for possible callers
that use the resulting data without checking the error code.

The all-ones data is what an x86 PC would see when an I/O
port is read that is not connected to any device.

     Arnd
Andy Shevchenko Oct. 11, 2024, 11:12 a.m. UTC | #3
On Fri, Oct 11, 2024 at 09:59:46AM +0000, Arnd Bergmann wrote:
> On Fri, Oct 11, 2024, at 09:53, Andy Shevchenko wrote:
> > On Fri, Oct 11, 2024 at 06:18:18AM +0000, Arnd Bergmann wrote:

...

> >> +	if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
> >> +		*value = BIT_MASK(width);
> >> +		return AE_NOT_IMPLEMENTED;
> >
> > Perhaps it has already been discussed, but why do we need to file value with
> > semi-garbage when we know it's invalid anyway?
> 
> It's not strictly necessary, just precaution for possible callers
> that use the resulting data without checking the error code.

Do you have any examples of that in the kernel?

> The all-ones data is what an x86 PC would see when an I/O
> port is read that is not connected to any device.

Yes, but it's not what your code does.
Rafael J. Wysocki Oct. 11, 2024, 11:28 a.m. UTC | #4
On Fri, Oct 11, 2024 at 1:12 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Fri, Oct 11, 2024 at 09:59:46AM +0000, Arnd Bergmann wrote:
> > On Fri, Oct 11, 2024, at 09:53, Andy Shevchenko wrote:
> > > On Fri, Oct 11, 2024 at 06:18:18AM +0000, Arnd Bergmann wrote:
>
> ...
>
> > >> +  if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
> > >> +          *value = BIT_MASK(width);
> > >> +          return AE_NOT_IMPLEMENTED;
> > >
> > > Perhaps it has already been discussed, but why do we need to file value with
> > > semi-garbage when we know it's invalid anyway?
> >
> > It's not strictly necessary, just precaution for possible callers
> > that use the resulting data without checking the error code.
>
> Do you have any examples of that in the kernel?

Yes, there are at least 2 cases.  May not be relevant, though.

> > The all-ones data is what an x86 PC would see when an I/O
> > port is read that is not connected to any device.
>
> Yes, but it's not what your code does.

Care to elaborate?
Arnd Bergmann Oct. 11, 2024, 11:40 a.m. UTC | #5
On Fri, Oct 11, 2024, at 11:12, Andy Shevchenko wrote:
> On Fri, Oct 11, 2024 at 09:59:46AM +0000, Arnd Bergmann wrote:
>> On Fri, Oct 11, 2024, at 09:53, Andy Shevchenko wrote:
>> > On Fri, Oct 11, 2024 at 06:18:18AM +0000, Arnd Bergmann wrote:
>> >> +	if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
>> >> +		*value = BIT_MASK(width);
>> >> +		return AE_NOT_IMPLEMENTED;
>> >
>> > Perhaps it has already been discussed, but why do we need to file value with
>> > semi-garbage when we know it's invalid anyway?
>> 
>> It's not strictly necessary, just precaution for possible callers
>> that use the resulting data without checking the error code.
>
> Do you have any examples of that in the kernel?


drivers/acpi/processor_throttling.c:            acpi_os_read_port((acpi_io_address) throttling->status_register.
--
drivers/cpufreq/acpi-cpufreq.c-
drivers/cpufreq/acpi-cpufreq.c: acpi_os_read_port(reg->address, &val, reg->bit_width);

$ git grep ^[^=]*acpi_os_read_port 
drivers/acpi/processor_throttling.c:            acpi_os_read_port(\ (acpi_io_address) throttling->status_register.
drivers/cpufreq/acpi-cpufreq.c: acpi_os_read_port(reg->address, &val, reg->bit_width);

>> The all-ones data is what an x86 PC would see when an I/O
>> port is read that is not connected to any device.
>
> Yes, but it's not what your code does.

My bad, I was confused about what BIT_MASK() does.
I'll change it to "GENMASK(width, 0)", which should
do what I intended.

      Arnd
Andy Shevchenko Oct. 11, 2024, 1:39 p.m. UTC | #6
On Fri, Oct 11, 2024 at 01:28:23PM +0200, Rafael J. Wysocki wrote:
> On Fri, Oct 11, 2024 at 1:12 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
> > On Fri, Oct 11, 2024 at 09:59:46AM +0000, Arnd Bergmann wrote:
> > > On Fri, Oct 11, 2024, at 09:53, Andy Shevchenko wrote:
> > > > On Fri, Oct 11, 2024 at 06:18:18AM +0000, Arnd Bergmann wrote:

...

> > > >> +  if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
> > > >> +          *value = BIT_MASK(width);
> > > >> +          return AE_NOT_IMPLEMENTED;
> > > >
> > > > Perhaps it has already been discussed, but why do we need to file value with
> > > > semi-garbage when we know it's invalid anyway?
> > >
> > > It's not strictly necessary, just precaution for possible callers
> > > that use the resulting data without checking the error code.
> >
> > Do you have any examples of that in the kernel?
> 
> Yes, there are at least 2 cases.  May not be relevant, though.

Btw, may be we even can add the error check to them, dunno...

> > > The all-ones data is what an x86 PC would see when an I/O
> > > port is read that is not connected to any device.
> >
> > Yes, but it's not what your code does.
> 
> Care to elaborate?

Sure, but it seems Arnd already figured out that he set one bit only somewhere
in the returned value, not what he stated in the explanation in this email
thread.
Andy Shevchenko Oct. 11, 2024, 1:41 p.m. UTC | #7
On Fri, Oct 11, 2024 at 11:40:05AM +0000, Arnd Bergmann wrote:
> On Fri, Oct 11, 2024, at 11:12, Andy Shevchenko wrote:
> > On Fri, Oct 11, 2024 at 09:59:46AM +0000, Arnd Bergmann wrote:
> >> On Fri, Oct 11, 2024, at 09:53, Andy Shevchenko wrote:
> >> > On Fri, Oct 11, 2024 at 06:18:18AM +0000, Arnd Bergmann wrote:
> >> >> +	if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
> >> >> +		*value = BIT_MASK(width);
> >> >> +		return AE_NOT_IMPLEMENTED;
> >> >
> >> > Perhaps it has already been discussed, but why do we need to file value with
> >> > semi-garbage when we know it's invalid anyway?
> >> 
> >> It's not strictly necessary, just precaution for possible callers
> >> that use the resulting data without checking the error code.
> >
> > Do you have any examples of that in the kernel?
> 
> drivers/acpi/processor_throttling.c:            acpi_os_read_port((acpi_io_address) throttling->status_register.
> --
> drivers/cpufreq/acpi-cpufreq.c-
> drivers/cpufreq/acpi-cpufreq.c: acpi_os_read_port(reg->address, &val, reg->bit_width);
> 
> $ git grep ^[^=]*acpi_os_read_port 
> drivers/acpi/processor_throttling.c:            acpi_os_read_port(\ (acpi_io_address) throttling->status_register.
> drivers/cpufreq/acpi-cpufreq.c: acpi_os_read_port(reg->address, &val, reg->bit_width);

May be we can add checks to them, but dunno...

> >> The all-ones data is what an x86 PC would see when an I/O
> >> port is read that is not connected to any device.
> >
> > Yes, but it's not what your code does.
> 
> My bad, I was confused about what BIT_MASK() does.
> I'll change it to "GENMASK(width, 0)", which should
> do what I intended.

Okay. Maybe also adding a comment that it's usual behaviour in response to
the read from non-existing IO port?

(Or for the curios it's all comes from the Data Bus on hardware being Open
 Drain an hence use of pull-up resistors and when there is no response on
 the bus, the default will be "All 1:s").
diff mbox series

Patch

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index b73b3aa92f3f..326b73ae77a9 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1017,7 +1017,8 @@  static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
 	*val = 0;
 	size = GET_BIT_WIDTH(reg);
 
-	if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+	if (IS_ENABLED(CONFIG_HAS_IOPORT) &&
+	    reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
 		u32 val_u32;
 		acpi_status status;
 
@@ -1090,7 +1091,8 @@  static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
 
 	size = GET_BIT_WIDTH(reg);
 
-	if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+	if (IS_ENABLED(CONFIG_HAS_IOPORT) &&
+	    reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
 		acpi_status status;
 
 		status = acpi_os_write_port((acpi_io_address)reg->address,
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 70af3fbbebe5..19342ccfabb9 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -642,6 +642,11 @@  acpi_status acpi_os_read_port(acpi_io_address port, u32 *value, u32 width)
 {
 	u32 dummy;
 
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT)) {
+		*value = BIT_MASK(width);
+		return AE_NOT_IMPLEMENTED;
+	}
+
 	if (value)
 		*value = 0;
 	else
@@ -665,6 +670,9 @@  EXPORT_SYMBOL(acpi_os_read_port);
 
 acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
 {
+	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
+		return AE_NOT_IMPLEMENTED;
+
 	if (width <= 8) {
 		outb(value, port);
 	} else if (width <= 16) {