ACPI updates for 6.17-rc1

- Printing the address in acpi_ex_trace_point() is either incorrect
    during early kernel boot or not really useful later when pathnames
    resolve properly, so stop doing it (Mario Limonciello)
 
  - Address several minor issues in the legacy ACPI proc interface (Andy
    Shevchenko)
 
  - Fix acpi_object union initialization in the ACPI processor driver to
    avoid using memory that contains leftover data (Sebastian Ott)
 
  - Make the ACPI processor perflib driver take the initial _PPC limit
    into account as appropriate (Jiayi Li)
 
  - Fix message formatting in the ACPI processor throttling driver and
    in the ACPI PCI link driver (Colin Ian King)
 
  - Clean up general ACPI PM domain handling (Rafael Wysocki)
 
  - Fix iomem-related sparse warnings in the APEI EINJ driver (Zaid
    Alali, Tony Luck)
 
  - Add EINJv2 error injection support to the APEI EINJ driver (Zaid
    Alali)
 
  - Fix memory corruption in error_type_set() in the APEI EINJ driver (Dan
    Carpenter)
 
  - Fix less than zero comparison on a size_t variable in the APEI EINJ
    driver (Colin Ian King)
 
  - Fix check and iounmap of an uninitialized pointer in the APEI EINJ
    driver (Colin Ian King)
 
  - Add TAINT_MACHINE_CHECK to the GHES panic path in APEI to improve
    diagnostics and post-mortem analysis (Breno Leitao)
 
  - Update APEI reviewer records and other ACPI-related information in
    MAINTAINERS as well as the contact information in the ACPI ABI
    documentation (Rafael Wysocki)
 
  - Fix the handling of synchronous uncorrected memory errors in APEI
    (Shuai Xue)
 
  - Remove an AudioDSP-related ID from the ACPI LPSS driver (Andy
    Shevchenko)
 
  - Replace sprintf()/scnprintf() with sysfs_emit() in the ACPI fan
    driver and update a debug message in fan_get_state_acpi4() (Eslam
    Khafagy, Abdelrahman Fekry, Sumeet Pawnikar)
 
  - Add Intel Wildcat Lake support to the ACPI DPTF driver (Srinivas
    Pandruvada)
 
  - Add more debug information regarding failing firmware updates to the
    ACPI pfr_update driver (Chen Yu)
 
  - Reduce the verbosity of the ACPI PRM (platform runtime mechanism)
    driver to avoid user confusion (Zhu Qiyu)
 
  - Replace sprintf() with sysfs_emit() in the ACPI TAD (time and alarm
    device) driver (Sukrut Heroorkar)
 
  - Enable CONFIG_ACPI_DEBUG by default to make it easier to get ACPI
    debug messages from OEM platforms (Mario Limonciello)
 
  - Fix parent device references in ASL examples in the ACPI
    documentation and fix spelling and style in the gpio-properties
    documentation in firmware-guide (Andy Shevchenko)
 
  - Fix typos in ACPI documentation and comments (Bjorn Helgaas)
 -----BEGIN PGP SIGNATURE-----
 
 iQFGBAABCAAwFiEEcM8Aw/RY0dgsiRUR7l+9nS/U47UFAmh/qzQSHHJqd0Byand5
 c29ja2kubmV0AAoJEO5fvZ0v1OO1wMgH/2vklBeGYjxSIIn0qfiv/SnSW5B1jEE4
 5vDuCpafesm7tZtwFB9v2mRf/8SvmJey2jfYgGnBMBlTW0JUP8eCVpRASvx1SCGH
 QwJFN3GCs3IjIvT2KlXeDIyQdfITIl3SNTXwTFl/ezYT0vo7VBeFtofeL9szaxlP
 rM1KeLE7lksjY9djT8PRwxOQ+EzuJ8uUGXYcHu797u0x5XB9ZiBDuKqngDicQONI
 DaRU3zXwOKPkQhldFN+9HPsDPvm7+8f1yiOEWzLnToTDggQDH4x2wdXJOOpAogoN
 qIYBIZG90drNj9USsWZvp0q/fT3OVbuHLuruDGieOCYUByBYddY9WHM=
 =5h+J
 -----END PGP SIGNATURE-----

Merge tag 'acpi-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI updates from Rafael Wysocki:
 "These update APEI (new EINJv2 error injection, assorted fixes), fix
  the ACPI processor driver, update the legacy ACPI /proc interface
  (multiple assorted fixes of minor issues) and several assorted ACPI
  drivers (minor fixes and cleanups):

   - Printing the address in acpi_ex_trace_point() is either incorrect
     during early kernel boot or not really useful later when pathnames
     resolve properly, so stop doing it (Mario Limonciello)

   - Address several minor issues in the legacy ACPI proc interface
     (Andy Shevchenko)

   - Fix acpi_object union initialization in the ACPI processor driver
     to avoid using memory that contains leftover data (Sebastian Ott)

   - Make the ACPI processor perflib driver take the initial _PPC limit
     into account as appropriate (Jiayi Li)

   - Fix message formatting in the ACPI processor throttling driver and
     in the ACPI PCI link driver (Colin Ian King)

   - Clean up general ACPI PM domain handling (Rafael Wysocki)

   - Fix iomem-related sparse warnings in the APEI EINJ driver (Zaid
     Alali, Tony Luck)

   - Add EINJv2 error injection support to the APEI EINJ driver (Zaid
     Alali)

   - Fix memory corruption in error_type_set() in the APEI EINJ driver
     (Dan Carpenter)

   - Fix less than zero comparison on a size_t variable in the APEI EINJ
     driver (Colin Ian King)

   - Fix check and iounmap of an uninitialized pointer in the APEI EINJ
     driver (Colin Ian King)

   - Add TAINT_MACHINE_CHECK to the GHES panic path in APEI to improve
     diagnostics and post-mortem analysis (Breno Leitao)

   - Update APEI reviewer records and other ACPI-related information in
     MAINTAINERS as well as the contact information in the ACPI ABI
     documentation (Rafael Wysocki)

   - Fix the handling of synchronous uncorrected memory errors in APEI
     (Shuai Xue)

   - Remove an AudioDSP-related ID from the ACPI LPSS driver (Andy
     Shevchenko)

   - Replace sprintf()/scnprintf() with sysfs_emit() in the ACPI fan
     driver and update a debug message in fan_get_state_acpi4() (Eslam
     Khafagy, Abdelrahman Fekry, Sumeet Pawnikar)

   - Add Intel Wildcat Lake support to the ACPI DPTF driver (Srinivas
     Pandruvada)

   - Add more debug information regarding failing firmware updates to
     the ACPI pfr_update driver (Chen Yu)

   - Reduce the verbosity of the ACPI PRM (platform runtime mechanism)
     driver to avoid user confusion (Zhu Qiyu)

   - Replace sprintf() with sysfs_emit() in the ACPI TAD (time and alarm
     device) driver (Sukrut Heroorkar)

   - Enable CONFIG_ACPI_DEBUG by default to make it easier to get ACPI
     debug messages from OEM platforms (Mario Limonciello)

   - Fix parent device references in ASL examples in the ACPI
     documentation and fix spelling and style in the gpio-properties
     documentation in firmware-guide (Andy Shevchenko)

   - Fix typos in ACPI documentation and comments (Bjorn Helgaas)"

* tag 'acpi-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (39 commits)
  ACPI: Fix typos
  ACPI/PCI: Remove space before newline
  ACPI: processor: throttling: Remove space before newline
  ACPI: processor: perflib: Fix initial _PPC limit application
  ACPI/PNP: Use my kernel.org address in MAINTAINERS and ABI docs
  ACPI: TAD: Replace sprintf() with sysfs_emit()
  ACPI: APEI: handle synchronous exceptions in task work
  ACPI: APEI: send SIGBUS to current task if synchronous memory error not recovered
  ACPI: APEI: MAINTAINERS: Update reviewers for APEI
  Documentation: ACPI: Fix parent device references
  ACPI: fan: Update debug message in fan_get_state_acpi4()
  ACPI: PRM: Reduce unnecessary printing to avoid user confusion
  ACPI: fan: Replace sprintf() with sysfs_emit()
  ACPI: APEI: EINJ: Fix trigger actions
  ACPI: processor: fix acpi_object initialization
  ACPI: APEI: GHES: add TAINT_MACHINE_CHECK on GHES panic path
  ACPI: LPSS: Remove AudioDSP related ID
  Documentation: firmware-guide: gpio-properties: Spelling and style fixes
  ACPI: fan: Replace sprintf()/scnprintf() with sysfs_emit() in show() functions
  ACPI: PM: Set .detach in acpi_general_pm_domain definition
  ...
This commit is contained in:
Linus Torvalds 2025-07-28 20:44:42 -07:00
commit 9bbf8e17d8
34 changed files with 567 additions and 204 deletions

View File

@ -1,6 +1,6 @@
What: /sys/bus/acpi/devices/.../path What: /sys/bus/acpi/devices/.../path
Date: December 2006 Date: December 2006
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
This attribute indicates the full path of ACPI namespace This attribute indicates the full path of ACPI namespace
object associated with the device object. For example, object associated with the device object. For example,
@ -12,7 +12,7 @@ Description:
What: /sys/bus/acpi/devices/.../modalias What: /sys/bus/acpi/devices/.../modalias
Date: July 2007 Date: July 2007
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
This attribute indicates the PNP IDs of the device object. This attribute indicates the PNP IDs of the device object.
That is acpi:HHHHHHHH:[CCCCCCC:]. Where each HHHHHHHH or That is acpi:HHHHHHHH:[CCCCCCC:]. Where each HHHHHHHH or
@ -20,7 +20,7 @@ Description:
What: /sys/bus/acpi/devices/.../hid What: /sys/bus/acpi/devices/.../hid
Date: April 2005 Date: April 2005
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
This attribute indicates the hardware ID (_HID) of the This attribute indicates the hardware ID (_HID) of the
device object. For example, PNP0103. device object. For example, PNP0103.
@ -29,14 +29,14 @@ Description:
What: /sys/bus/acpi/devices/.../description What: /sys/bus/acpi/devices/.../description
Date: October 2012 Date: October 2012
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
This attribute contains the output of the device object's This attribute contains the output of the device object's
_STR control method, if present. _STR control method, if present.
What: /sys/bus/acpi/devices/.../adr What: /sys/bus/acpi/devices/.../adr
Date: October 2012 Date: October 2012
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
This attribute contains the output of the device object's This attribute contains the output of the device object's
_ADR control method, which is present for ACPI device _ADR control method, which is present for ACPI device
@ -45,14 +45,14 @@ Description:
What: /sys/bus/acpi/devices/.../uid What: /sys/bus/acpi/devices/.../uid
Date: October 2012 Date: October 2012
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
This attribute contains the output of the device object's This attribute contains the output of the device object's
_UID control method, if present. _UID control method, if present.
What: /sys/bus/acpi/devices/.../eject What: /sys/bus/acpi/devices/.../eject
Date: December 2006 Date: December 2006
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
Writing 1 to this attribute will trigger hot removal of Writing 1 to this attribute will trigger hot removal of
this device object. This file exists for every device this device object. This file exists for every device
@ -60,7 +60,7 @@ Description:
What: /sys/bus/acpi/devices/.../status What: /sys/bus/acpi/devices/.../status
Date: Jan, 2014 Date: Jan, 2014
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
(RO) Returns the ACPI device status: enabled, disabled or (RO) Returns the ACPI device status: enabled, disabled or
functioning or present, if the method _STA is present. functioning or present, if the method _STA is present.
@ -90,7 +90,7 @@ Description:
What: /sys/bus/acpi/devices/.../hrv What: /sys/bus/acpi/devices/.../hrv
Date: Apr, 2016 Date: Apr, 2016
Contact: Rafael J. Wysocki <rjw@rjwysocki.net> Contact: Rafael J. Wysocki <rafael@kernel.org>
Description: Description:
(RO) Allows users to read the hardware version of non-PCI (RO) Allows users to read the hardware version of non-PCI
hardware, if the _HRV control method is present. It is mostly hardware, if the _HRV control method is present. It is mostly

View File

@ -108,15 +108,15 @@ Description:
number of a "General Purpose Events" (GPE). number of a "General Purpose Events" (GPE).
A GPE vectors to a specified handler in AML, which A GPE vectors to a specified handler in AML, which
can do a anything the BIOS writer wants from can do anything the BIOS writer wants from
OS context. GPE 0x12, for example, would vector OS context. GPE 0x12, for example, would vector
to a level or edge handler called _L12 or _E12. to a level or edge handler called _L12 or _E12.
The handler may do its business and return. The handler may do its business and return.
Or the handler may send send a Notify event Or the handler may send a Notify event
to a Linux device driver registered on an ACPI device, to a Linux device driver registered on an ACPI device,
such as a battery, or a processor. such as a battery, or a processor.
To figure out where all the SCI's are coming from, To figure out where all the SCIs are coming from,
/sys/firmware/acpi/interrupts contains a file listing /sys/firmware/acpi/interrupts contains a file listing
every possible source, and the count of how many every possible source, and the count of how many
times it has triggered:: times it has triggered::

View File

@ -59,6 +59,9 @@ The following files belong to it:
0x00000200 Platform Correctable 0x00000200 Platform Correctable
0x00000400 Platform Uncorrectable non-fatal 0x00000400 Platform Uncorrectable non-fatal
0x00000800 Platform Uncorrectable fatal 0x00000800 Platform Uncorrectable fatal
V2_0x00000001 EINJV2 Processor Error
V2_0x00000002 EINJV2 Memory Error
V2_0x00000004 EINJV2 PCI Express Error
================ =================================== ================ ===================================
The format of the file contents are as above, except present are only The format of the file contents are as above, except present are only
@ -88,6 +91,8 @@ The following files belong to it:
Memory address and mask valid (param1 and param2). Memory address and mask valid (param1 and param2).
Bit 2 Bit 2
PCIe (seg,bus,dev,fn) valid (see param4 below). PCIe (seg,bus,dev,fn) valid (see param4 below).
Bit 3
EINJv2 extension structure is valid
If set to zero, legacy behavior is mimicked where the type of If set to zero, legacy behavior is mimicked where the type of
injection specifies just one bit set, and param1 is multiplexed. injection specifies just one bit set, and param1 is multiplexed.
@ -122,6 +127,13 @@ The following files belong to it:
this actually works depends on what operations the BIOS actually this actually works depends on what operations the BIOS actually
includes in the trigger phase. includes in the trigger phase.
- component_id0 .. component_idN, component_syndrome0 .. component_syndromeN
These files are used to set the "Component Array" field
of the EINJv2 Extension Structure. Each holds a 128-bit
hex value. Writing just a newline to any of these files
sets an invalid (all-ones) value.
CXL error types are supported from ACPI 6.5 onwards (given a CXL port CXL error types are supported from ACPI 6.5 onwards (given a CXL port
is present). The EINJ user interface for CXL error types is at is present). The EINJ user interface for CXL error types is at
<debugfs mount point>/cxl. The following files belong to it: <debugfs mount point>/cxl. The following files belong to it:
@ -194,6 +206,27 @@ An error injection example::
# echo 0x8 > error_type # Choose correctable memory error # echo 0x8 > error_type # Choose correctable memory error
# echo 1 > error_inject # Inject now # echo 1 > error_inject # Inject now
An EINJv2 error injection example::
# cd /sys/kernel/debug/apei/einj
# cat available_error_type # See which errors can be injected
0x00000002 Processor Uncorrectable non-fatal
0x00000008 Memory Correctable
0x00000010 Memory Uncorrectable non-fatal
V2_0x00000001 EINJV2 Processor Error
V2_0x00000002 EINJV2 Memory Error
# echo 0x12345000 > param1 # Set memory address for injection
# echo 0xfffffffffffff000 > param2 # Range - anywhere in this page
# echo 0x1 > component_id0 # First device ID
# echo 0x4 > component_syndrome0 # First error syndrome
# echo 0x2 > component_id1 # Second device ID
# echo 0x4 > component_syndrome1 # Second error syndrome
# echo '' > component_id2 # Mark id2 invalid to terminate list
# echo V2_0x2 > error_type # Choose EINJv2 memory error
# echo 0xa > flags # set flags to indicate EINJv2
# echo 1 > error_inject # Inject now
You should see something like this in dmesg:: You should see something like this in dmesg::
[22715.830801] EDAC sbridge MC3: HANDLING MCE MEMORY ERROR [22715.830801] EDAC sbridge MC3: HANDLING MCE MEMORY ERROR

View File

@ -6,7 +6,7 @@ _DSD Device Properties Related to GPIO
With the release of ACPI 5.1, the _DSD configuration object finally With the release of ACPI 5.1, the _DSD configuration object finally
allows names to be given to GPIOs (and other things as well) returned allows names to be given to GPIOs (and other things as well) returned
by _CRS. Previously, we were only able to use an integer index to find by _CRS. Previously we were only able to use an integer index to find
the corresponding GPIO, which is pretty error prone (it depends on the corresponding GPIO, which is pretty error prone (it depends on
the _CRS output ordering, for example). the _CRS output ordering, for example).
@ -49,11 +49,11 @@ index
pin pin
Pin in the GpioIo()/GpioInt() resource. Typically this is zero. Pin in the GpioIo()/GpioInt() resource. Typically this is zero.
active_low active_low
If 1, the GPIO is marked as active_low. If 1, the GPIO is marked as active-low.
Since ACPI GpioIo() resource does not have a field saying whether it is Since ACPI GpioIo() resource does not have a field saying whether it is
active low or high, the "active_low" argument can be used here. Setting active-low or active-high, the "active_low" argument can be used here.
it to 1 marks the GPIO as active low. Setting it to 1 marks the GPIO as active-low.
Note, active_low in _DSD does not make sense for GpioInt() resource and Note, active_low in _DSD does not make sense for GpioInt() resource and
must be 0. GpioInt() resource has its own means of defining it. must be 0. GpioInt() resource has its own means of defining it.
@ -92,8 +92,8 @@ and polarity settings. The table below shows the expectations:
| | Low | as low, assuming active | | | Low | as low, assuming active |
+-------------+-------------+-----------------------------------------------+ +-------------+-------------+-----------------------------------------------+
That said, for our above example the both GPIOs, since the bias setting That said, for our above example, since the bias setting is explicit and
is explicit and _DSD is present, will be treated as active with a high _DSD is present, both GPIOs will be treated as active with a high
polarity and Linux will configure the pins in this state until a driver polarity and Linux will configure the pins in this state until a driver
reprograms them differently. reprograms them differently.
@ -231,8 +231,8 @@ In those cases ACPI device identification objects, _HID, _CID, _CLS, _SUB, _HRV,
available to the driver can be used to identify the device and that is supposed available to the driver can be used to identify the device and that is supposed
to be sufficient to determine the meaning and purpose of all of the GPIO lines to be sufficient to determine the meaning and purpose of all of the GPIO lines
listed by the GpioIo()/GpioInt() resources returned by _CRS. In other words, listed by the GpioIo()/GpioInt() resources returned by _CRS. In other words,
the driver is supposed to know what to use the GpioIo()/GpioInt() resources for the driver is supposed to know what to use from the GpioIo()/GpioInt() resources
once it has identified the device. Having done that, it can simply assign names for once it has identified the device. Having done that, it can simply assign names
to the GPIO lines it is going to use and provide the GPIO subsystem with a to the GPIO lines it is going to use and provide the GPIO subsystem with a
mapping between those names and the ACPI GPIO resources corresponding to them. mapping between those names and the ACPI GPIO resources corresponding to them.
@ -270,7 +270,7 @@ Using the _CRS fallback
If a device does not have _DSD or the driver does not create ACPI GPIO If a device does not have _DSD or the driver does not create ACPI GPIO
mapping, the Linux GPIO framework refuses to return any GPIOs. This is mapping, the Linux GPIO framework refuses to return any GPIOs. This is
because the driver does not know what it actually gets. For example if we because the driver does not know what it actually gets. For example, if we
have a device like below:: have a device like below::
Device (BTH) Device (BTH)
@ -292,7 +292,7 @@ The driver might expect to get the right GPIO when it does::
...error handling... ...error handling...
but since there is no way to know the mapping between "reset" and but since there is no way to know the mapping between "reset" and
the GpioIo() in _CRS desc will hold ERR_PTR(-ENOENT). the GpioIo() in _CRS the desc will hold ERR_PTR(-ENOENT).
The driver author can solve this by passing the mapping explicitly The driver author can solve this by passing the mapping explicitly
(this is the recommended way and it's documented in the above chapter). (this is the recommended way and it's documented in the above chapter).
@ -318,15 +318,15 @@ Case 1::
desc = gpiod_get(dev, "non-null-connection-id", flags); desc = gpiod_get(dev, "non-null-connection-id", flags);
desc = gpiod_get_index(dev, "non-null-connection-id", index, flags); desc = gpiod_get_index(dev, "non-null-connection-id", index, flags);
Case 1 assumes that corresponding ACPI device description must have
defined device properties and will prevent from getting any GPIO resources
otherwise.
Case 2:: Case 2::
desc = gpiod_get(dev, NULL, flags); desc = gpiod_get(dev, NULL, flags);
desc = gpiod_get_index(dev, NULL, index, flags); desc = gpiod_get_index(dev, NULL, index, flags);
Case 1 assumes that corresponding ACPI device description must have
defined device properties and will prevent to getting any GPIO resources
otherwise.
Case 2 explicitly tells GPIO core to look for resources in _CRS. Case 2 explicitly tells GPIO core to look for resources in _CRS.
Be aware that gpiod_get_index() in cases 1 and 2, assuming that there Be aware that gpiod_get_index() in cases 1 and 2, assuming that there

View File

@ -14,7 +14,7 @@ Consider this topology::
| | | 0x70 |--CH01--> i2c client B (0x50) | | | 0x70 |--CH01--> i2c client B (0x50)
+------+ +------+ +------+ +------+
which corresponds to the following ASL:: which corresponds to the following ASL (in the scope of \_SB)::
Device (SMB1) Device (SMB1)
{ {
@ -24,7 +24,7 @@ which corresponds to the following ASL::
Name (_HID, ...) Name (_HID, ...)
Name (_CRS, ResourceTemplate () { Name (_CRS, ResourceTemplate () {
I2cSerialBus (0x70, ControllerInitiated, I2C_SPEED, I2cSerialBus (0x70, ControllerInitiated, I2C_SPEED,
AddressingMode7Bit, "^SMB1", 0x00, AddressingMode7Bit, "\\_SB.SMB1", 0x00,
ResourceConsumer,,) ResourceConsumer,,)
} }
@ -37,7 +37,7 @@ which corresponds to the following ASL::
Name (_HID, ...) Name (_HID, ...)
Name (_CRS, ResourceTemplate () { Name (_CRS, ResourceTemplate () {
I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED,
AddressingMode7Bit, "^CH00", 0x00, AddressingMode7Bit, "\\_SB.SMB1.CH00", 0x00,
ResourceConsumer,,) ResourceConsumer,,)
} }
} }
@ -52,7 +52,7 @@ which corresponds to the following ASL::
Name (_HID, ...) Name (_HID, ...)
Name (_CRS, ResourceTemplate () { Name (_CRS, ResourceTemplate () {
I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED,
AddressingMode7Bit, "^CH01", 0x00, AddressingMode7Bit, "\\_SB.SMB1.CH01", 0x00,
ResourceConsumer,,) ResourceConsumer,,)
} }
} }

View File

@ -306,16 +306,17 @@ F: tools/power/acpi/
ACPI APEI ACPI APEI
M: "Rafael J. Wysocki" <rafael@kernel.org> M: "Rafael J. Wysocki" <rafael@kernel.org>
R: Len Brown <lenb@kernel.org>
R: James Morse <james.morse@arm.com>
R: Tony Luck <tony.luck@intel.com> R: Tony Luck <tony.luck@intel.com>
R: Borislav Petkov <bp@alien8.de> R: Borislav Petkov <bp@alien8.de>
R: Hanjun Guo <guohanjun@huawei.com>
R: Mauro Carvalho Chehab <mchehab@kernel.org>
R: Shuai Xue <xueshuai@linux.alibaba.com>
L: linux-acpi@vger.kernel.org L: linux-acpi@vger.kernel.org
F: drivers/acpi/apei/ F: drivers/acpi/apei/
ACPI COMPONENT ARCHITECTURE (ACPICA) ACPI COMPONENT ARCHITECTURE (ACPICA)
M: "Rafael J. Wysocki" <rafael@kernel.org>
M: Robert Moore <robert.moore@intel.com> M: Robert Moore <robert.moore@intel.com>
M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
L: linux-acpi@vger.kernel.org L: linux-acpi@vger.kernel.org
L: acpica-devel@lists.linux.dev L: acpica-devel@lists.linux.dev
S: Supported S: Supported
@ -19768,7 +19769,7 @@ F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.yaml
F: drivers/iio/magnetometer/rm3100* F: drivers/iio/magnetometer/rm3100*
PNP SUPPORT PNP SUPPORT
M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> M: "Rafael J. Wysocki" <rafael@kernel.org>
L: linux-acpi@vger.kernel.org L: linux-acpi@vger.kernel.org
S: Maintained S: Maintained
F: drivers/pnp/ F: drivers/pnp/

View File

@ -394,6 +394,7 @@ config ACPI_TABLE_OVERRIDE_VIA_BUILTIN_INITRD
config ACPI_DEBUG config ACPI_DEBUG
bool "Debug Statements" bool "Debug Statements"
default y
help help
The ACPI subsystem can produce debug output. Saying Y enables this The ACPI subsystem can produce debug output. Saying Y enables this
output and increases the kernel size by around 50K. output and increases the kernel size by around 50K.

View File

@ -275,7 +275,7 @@ static inline int acpi_processor_hotadd_init(struct acpi_processor *pr,
static int acpi_processor_get_info(struct acpi_device *device) static int acpi_processor_get_info(struct acpi_device *device)
{ {
union acpi_object object = { 0 }; union acpi_object object = { .processor = { 0 } };
struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
struct acpi_processor *pr = acpi_driver_data(device); struct acpi_processor *pr = acpi_driver_data(device);
int device_declaration = 0; int device_declaration = 0;

View File

@ -233,7 +233,7 @@ static ssize_t time_show(struct device *dev, struct device_attribute *attr,
if (ret) if (ret)
return ret; return ret;
return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", return sysfs_emit(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n",
rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second,
rt.tz, rt.daylight); rt.tz, rt.daylight);
} }
@ -428,7 +428,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
{ {
struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
return sprintf(buf, "0x%02X\n", dd->capabilities); return sysfs_emit(buf, "0x%02X\n", dd->capabilities);
} }
static DEVICE_ATTR_RO(caps); static DEVICE_ATTR_RO(caps);

View File

@ -136,9 +136,9 @@ acpi_ex_trace_point(acpi_trace_event_type type,
if (pathname) { if (pathname) {
ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT, ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
"%s %s [0x%p:%s] execution.\n", "%s %s [%s] execution.\n",
acpi_ex_get_trace_event_name(type), acpi_ex_get_trace_event_name(type),
begin ? "Begin" : "End", aml, pathname)); begin ? "Begin" : "End", pathname));
} else { } else {
ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT, ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
"%s %s [0x%p] execution.\n", "%s %s [0x%p] execution.\n",

View File

@ -131,7 +131,7 @@ static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus)
int apei_osc_setup(void); int apei_osc_setup(void);
int einj_get_available_error_type(u32 *type); int einj_get_available_error_type(u32 *type, int einj_action);
int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
u64 param4); u64 param4);
int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2, int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,

View File

@ -33,6 +33,8 @@
#define SLEEP_UNIT_MAX 5000 /* 5ms */ #define SLEEP_UNIT_MAX 5000 /* 5ms */
/* Firmware should respond within 1 seconds */ /* Firmware should respond within 1 seconds */
#define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC) #define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC)
#define COMPONENT_LEN 16
#define ACPI65_EINJV2_SUPP BIT(30)
#define ACPI5_VENDOR_BIT BIT(31) #define ACPI5_VENDOR_BIT BIT(31)
#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ #define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
ACPI_EINJ_MEMORY_UNCORRECTABLE | \ ACPI_EINJ_MEMORY_UNCORRECTABLE | \
@ -49,6 +51,28 @@
*/ */
static int acpi5; static int acpi5;
struct syndrome_array {
union {
u8 acpi_id[COMPONENT_LEN];
u8 device_id[COMPONENT_LEN];
u8 pcie_sbdf[COMPONENT_LEN];
u8 vendor_id[COMPONENT_LEN];
} comp_id;
union {
u8 proc_synd[COMPONENT_LEN];
u8 mem_synd[COMPONENT_LEN];
u8 pcie_synd[COMPONENT_LEN];
u8 vendor_synd[COMPONENT_LEN];
} comp_synd;
};
struct einjv2_extension_struct {
u32 length;
u16 revision;
u16 component_arr_count;
struct syndrome_array component_arr[] __counted_by(component_arr_count);
};
struct set_error_type_with_address { struct set_error_type_with_address {
u32 type; u32 type;
u32 vendor_extension; u32 vendor_extension;
@ -57,11 +81,13 @@ struct set_error_type_with_address {
u64 memory_address; u64 memory_address;
u64 memory_address_range; u64 memory_address_range;
u32 pcie_sbdf; u32 pcie_sbdf;
struct einjv2_extension_struct einjv2_struct;
}; };
enum { enum {
SETWA_FLAGS_APICID = 1, SETWA_FLAGS_APICID = 1,
SETWA_FLAGS_MEM = 2, SETWA_FLAGS_MEM = 2,
SETWA_FLAGS_PCIE_SBDF = 4, SETWA_FLAGS_PCIE_SBDF = 4,
SETWA_FLAGS_EINJV2 = 8,
}; };
/* /*
@ -83,7 +109,10 @@ static struct debugfs_blob_wrapper vendor_blob;
static struct debugfs_blob_wrapper vendor_errors; static struct debugfs_blob_wrapper vendor_errors;
static char vendor_dev[64]; static char vendor_dev[64];
static u32 max_nr_components;
static u32 available_error_type; static u32 available_error_type;
static u32 available_error_type_v2;
static struct syndrome_array *syndrome_data;
/* /*
* Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
@ -151,7 +180,9 @@ static DEFINE_MUTEX(einj_mutex);
*/ */
bool einj_initialized __ro_after_init; bool einj_initialized __ro_after_init;
static void *einj_param; static void __iomem *einj_param;
static u32 v5param_size;
static bool is_v2;
static void einj_exec_ctx_init(struct apei_exec_context *ctx) static void einj_exec_ctx_init(struct apei_exec_context *ctx)
{ {
@ -159,13 +190,13 @@ static void einj_exec_ctx_init(struct apei_exec_context *ctx)
EINJ_TAB_ENTRY(einj_tab), einj_tab->entries); EINJ_TAB_ENTRY(einj_tab), einj_tab->entries);
} }
static int __einj_get_available_error_type(u32 *type) static int __einj_get_available_error_type(u32 *type, int einj_action)
{ {
struct apei_exec_context ctx; struct apei_exec_context ctx;
int rc; int rc;
einj_exec_ctx_init(&ctx); einj_exec_ctx_init(&ctx);
rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE); rc = apei_exec_run(&ctx, einj_action);
if (rc) if (rc)
return rc; return rc;
*type = apei_exec_ctx_get_output(&ctx); *type = apei_exec_ctx_get_output(&ctx);
@ -174,17 +205,34 @@ static int __einj_get_available_error_type(u32 *type)
} }
/* Get error injection capabilities of the platform */ /* Get error injection capabilities of the platform */
int einj_get_available_error_type(u32 *type) int einj_get_available_error_type(u32 *type, int einj_action)
{ {
int rc; int rc;
mutex_lock(&einj_mutex); mutex_lock(&einj_mutex);
rc = __einj_get_available_error_type(type); rc = __einj_get_available_error_type(type, einj_action);
mutex_unlock(&einj_mutex); mutex_unlock(&einj_mutex);
return rc; return rc;
} }
static int einj_get_available_error_types(u32 *type1, u32 *type2)
{
int rc;
rc = einj_get_available_error_type(type1, ACPI_EINJ_GET_ERROR_TYPE);
if (rc)
return rc;
if (*type1 & ACPI65_EINJV2_SUPP) {
rc = einj_get_available_error_type(type2,
ACPI_EINJV2_GET_ERROR_TYPE);
if (rc)
return rc;
}
return 0;
}
static int einj_timedout(u64 *t) static int einj_timedout(u64 *t)
{ {
if ((s64)*t < SLEEP_UNIT_MIN) { if ((s64)*t < SLEEP_UNIT_MIN) {
@ -216,24 +264,26 @@ static void check_vendor_extension(u64 paddr,
struct set_error_type_with_address *v5param) struct set_error_type_with_address *v5param)
{ {
int offset = v5param->vendor_extension; int offset = v5param->vendor_extension;
struct vendor_error_type_extension *v; struct vendor_error_type_extension v;
struct vendor_error_type_extension __iomem *p;
u32 sbdf; u32 sbdf;
if (!offset) if (!offset)
return; return;
v = acpi_os_map_iomem(paddr + offset, sizeof(*v)); p = acpi_os_map_iomem(paddr + offset, sizeof(*p));
if (!v) if (!p)
return; return;
get_oem_vendor_struct(paddr, offset, v); memcpy_fromio(&v, p, sizeof(v));
sbdf = v->pcie_sbdf; get_oem_vendor_struct(paddr, offset, &v);
sbdf = v.pcie_sbdf;
sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
sbdf >> 24, (sbdf >> 16) & 0xff, sbdf >> 24, (sbdf >> 16) & 0xff,
(sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
v->vendor_id, v->device_id, v->rev_id); v.vendor_id, v.device_id, v.rev_id);
acpi_os_unmap_iomem(v, sizeof(*v)); acpi_os_unmap_iomem(p, sizeof(v));
} }
static void *einj_get_parameter_address(void) static void __iomem *einj_get_parameter_address(void)
{ {
int i; int i;
u64 pa_v4 = 0, pa_v5 = 0; u64 pa_v4 = 0, pa_v5 = 0;
@ -254,26 +304,50 @@ static void *einj_get_parameter_address(void)
entry++; entry++;
} }
if (pa_v5) { if (pa_v5) {
struct set_error_type_with_address *v5param; struct set_error_type_with_address v5param;
struct set_error_type_with_address __iomem *p;
v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param)); v5param_size = sizeof(v5param);
if (v5param) { p = acpi_os_map_iomem(pa_v5, sizeof(*p));
if (p) {
int offset, len;
memcpy_fromio(&v5param, p, v5param_size);
acpi5 = 1; acpi5 = 1;
check_vendor_extension(pa_v5, v5param); check_vendor_extension(pa_v5, &v5param);
return v5param; if (available_error_type & ACPI65_EINJV2_SUPP) {
len = v5param.einjv2_struct.length;
offset = offsetof(struct einjv2_extension_struct, component_arr);
max_nr_components = (len - offset) /
sizeof(v5param.einjv2_struct.component_arr[0]);
/*
* The first call to acpi_os_map_iomem above does not include the
* component array, instead it is used to read and calculate maximum
* number of components supported by the system. Below, the mapping
* is expanded to include the component array.
*/
acpi_os_unmap_iomem(p, v5param_size);
offset = offsetof(struct set_error_type_with_address, einjv2_struct);
v5param_size = offset + struct_size(&v5param.einjv2_struct,
component_arr, max_nr_components);
p = acpi_os_map_iomem(pa_v5, v5param_size);
}
return p;
} }
} }
if (param_extension && pa_v4) { if (param_extension && pa_v4) {
struct einj_parameter *v4param; struct einj_parameter v4param;
struct einj_parameter __iomem *p;
v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param)); p = acpi_os_map_iomem(pa_v4, sizeof(*p));
if (!v4param) if (!p)
return NULL; return NULL;
if (v4param->reserved1 || v4param->reserved2) { memcpy_fromio(&v4param, p, sizeof(v4param));
acpi_os_unmap_iomem(v4param, sizeof(*v4param)); if (v4param.reserved1 || v4param.reserved2) {
acpi_os_unmap_iomem(p, sizeof(v4param));
return NULL; return NULL;
} }
return v4param; return p;
} }
return NULL; return NULL;
@ -319,7 +393,8 @@ static struct acpi_generic_address *einj_get_trigger_parameter_region(
static int __einj_error_trigger(u64 trigger_paddr, u32 type, static int __einj_error_trigger(u64 trigger_paddr, u32 type,
u64 param1, u64 param2) u64 param1, u64 param2)
{ {
struct acpi_einj_trigger *trigger_tab = NULL; struct acpi_einj_trigger trigger_tab;
struct acpi_einj_trigger *full_trigger_tab;
struct apei_exec_context trigger_ctx; struct apei_exec_context trigger_ctx;
struct apei_resources trigger_resources; struct apei_resources trigger_resources;
struct acpi_whea_header *trigger_entry; struct acpi_whea_header *trigger_entry;
@ -327,54 +402,60 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
u32 table_size; u32 table_size;
int rc = -EIO; int rc = -EIO;
struct acpi_generic_address *trigger_param_region = NULL; struct acpi_generic_address *trigger_param_region = NULL;
struct acpi_einj_trigger __iomem *p = NULL;
r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), r = request_mem_region(trigger_paddr, sizeof(trigger_tab),
"APEI EINJ Trigger Table"); "APEI EINJ Trigger Table");
if (!r) { if (!r) {
pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n", pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n",
(unsigned long long)trigger_paddr, (unsigned long long)trigger_paddr,
(unsigned long long)trigger_paddr + (unsigned long long)trigger_paddr +
sizeof(*trigger_tab) - 1); sizeof(trigger_tab) - 1);
goto out; goto out;
} }
trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); p = ioremap_cache(trigger_paddr, sizeof(*p));
if (!trigger_tab) { if (!p) {
pr_err("Failed to map trigger table!\n"); pr_err("Failed to map trigger table!\n");
goto out_rel_header; goto out_rel_header;
} }
rc = einj_check_trigger_header(trigger_tab); memcpy_fromio(&trigger_tab, p, sizeof(trigger_tab));
rc = einj_check_trigger_header(&trigger_tab);
if (rc) { if (rc) {
pr_warn(FW_BUG "Invalid trigger error action table.\n"); pr_warn(FW_BUG "Invalid trigger error action table.\n");
goto out_rel_header; goto out_rel_header;
} }
/* No action structures in the TRIGGER_ERROR table, nothing to do */ /* No action structures in the TRIGGER_ERROR table, nothing to do */
if (!trigger_tab->entry_count) if (!trigger_tab.entry_count)
goto out_rel_header; goto out_rel_header;
rc = -EIO; rc = -EIO;
table_size = trigger_tab->table_size; table_size = trigger_tab.table_size;
r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), full_trigger_tab = kmalloc(table_size, GFP_KERNEL);
table_size - sizeof(*trigger_tab), if (!full_trigger_tab)
goto out_rel_header;
r = request_mem_region(trigger_paddr + sizeof(trigger_tab),
table_size - sizeof(trigger_tab),
"APEI EINJ Trigger Table"); "APEI EINJ Trigger Table");
if (!r) { if (!r) {
pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
(unsigned long long)trigger_paddr + sizeof(*trigger_tab), (unsigned long long)trigger_paddr + sizeof(trigger_tab),
(unsigned long long)trigger_paddr + table_size - 1); (unsigned long long)trigger_paddr + table_size - 1);
goto out_rel_header; goto out_free_trigger_tab;
} }
iounmap(trigger_tab); iounmap(p);
trigger_tab = ioremap_cache(trigger_paddr, table_size); p = ioremap_cache(trigger_paddr, table_size);
if (!trigger_tab) { if (!p) {
pr_err("Failed to map trigger table!\n"); pr_err("Failed to map trigger table!\n");
goto out_rel_entry; goto out_rel_entry;
} }
memcpy_fromio(full_trigger_tab, p, table_size);
trigger_entry = (struct acpi_whea_header *) trigger_entry = (struct acpi_whea_header *)
((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); ((char *)full_trigger_tab + sizeof(struct acpi_einj_trigger));
apei_resources_init(&trigger_resources); apei_resources_init(&trigger_resources);
apei_exec_ctx_init(&trigger_ctx, einj_ins_type, apei_exec_ctx_init(&trigger_ctx, einj_ins_type,
ARRAY_SIZE(einj_ins_type), ARRAY_SIZE(einj_ins_type),
trigger_entry, trigger_tab->entry_count); trigger_entry, trigger_tab.entry_count);
rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources); rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources);
if (rc) if (rc)
goto out_fini; goto out_fini;
@ -392,7 +473,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
apei_resources_init(&addr_resources); apei_resources_init(&addr_resources);
trigger_param_region = einj_get_trigger_parameter_region( trigger_param_region = einj_get_trigger_parameter_region(
trigger_tab, param1, param2); full_trigger_tab, param1, param2);
if (trigger_param_region) { if (trigger_param_region) {
rc = apei_resources_add(&addr_resources, rc = apei_resources_add(&addr_resources,
trigger_param_region->address, trigger_param_region->address,
@ -421,23 +502,33 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
out_fini: out_fini:
apei_resources_fini(&trigger_resources); apei_resources_fini(&trigger_resources);
out_rel_entry: out_rel_entry:
release_mem_region(trigger_paddr + sizeof(*trigger_tab), release_mem_region(trigger_paddr + sizeof(trigger_tab),
table_size - sizeof(*trigger_tab)); table_size - sizeof(trigger_tab));
out_free_trigger_tab:
kfree(full_trigger_tab);
out_rel_header: out_rel_header:
release_mem_region(trigger_paddr, sizeof(*trigger_tab)); release_mem_region(trigger_paddr, sizeof(trigger_tab));
out: out:
if (trigger_tab) if (p)
iounmap(trigger_tab); iounmap(p);
return rc; return rc;
} }
static bool is_end_of_list(u8 *val)
{
for (int i = 0; i < COMPONENT_LEN; ++i) {
if (val[i] != 0xFF)
return false;
}
return true;
}
static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
u64 param3, u64 param4) u64 param3, u64 param4)
{ {
struct apei_exec_context ctx; struct apei_exec_context ctx;
u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
int rc; int i, rc;
einj_exec_ctx_init(&ctx); einj_exec_ctx_init(&ctx);
@ -446,8 +537,10 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
return rc; return rc;
apei_exec_ctx_set_input(&ctx, type); apei_exec_ctx_set_input(&ctx, type);
if (acpi5) { if (acpi5) {
struct set_error_type_with_address *v5param = einj_param; struct set_error_type_with_address *v5param;
v5param = kmalloc(v5param_size, GFP_KERNEL);
memcpy_fromio(v5param, einj_param, v5param_size);
v5param->type = type; v5param->type = type;
if (type & ACPI5_VENDOR_BIT) { if (type & ACPI5_VENDOR_BIT) {
switch (vendor_flags) { switch (vendor_flags) {
@ -467,8 +560,21 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
v5param->flags = flags; v5param->flags = flags;
v5param->memory_address = param1; v5param->memory_address = param1;
v5param->memory_address_range = param2; v5param->memory_address_range = param2;
if (is_v2) {
for (i = 0; i < max_nr_components; i++) {
if (is_end_of_list(syndrome_data[i].comp_id.acpi_id))
break;
v5param->einjv2_struct.component_arr[i].comp_id =
syndrome_data[i].comp_id;
v5param->einjv2_struct.component_arr[i].comp_synd =
syndrome_data[i].comp_synd;
}
v5param->einjv2_struct.component_arr_count = i;
} else {
v5param->apicid = param3; v5param->apicid = param3;
v5param->pcie_sbdf = param4; v5param->pcie_sbdf = param4;
}
} else { } else {
switch (type) { switch (type) {
case ACPI_EINJ_PROCESSOR_CORRECTABLE: case ACPI_EINJ_PROCESSOR_CORRECTABLE:
@ -492,15 +598,19 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
break; break;
} }
} }
memcpy_toio(einj_param, v5param, v5param_size);
kfree(v5param);
} else { } else {
rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
if (rc) if (rc)
return rc; return rc;
if (einj_param) { if (einj_param) {
struct einj_parameter *v4param = einj_param; struct einj_parameter v4param;
v4param->param1 = param1; memcpy_fromio(&v4param, einj_param, sizeof(v4param));
v4param->param2 = param2; v4param.param1 = param1;
v4param.param2 = param2;
memcpy_toio(einj_param, &v4param, sizeof(v4param));
} }
} }
rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION);
@ -551,10 +661,15 @@ int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
u64 base_addr, size; u64 base_addr, size;
/* If user manually set "flags", make sure it is legal */ /* If user manually set "flags", make sure it is legal */
if (flags && (flags & if (flags && (flags & ~(SETWA_FLAGS_APICID | SETWA_FLAGS_MEM |
~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF))) SETWA_FLAGS_PCIE_SBDF | SETWA_FLAGS_EINJV2)))
return -EINVAL; return -EINVAL;
/* check if type is a valid EINJv2 error type */
if (is_v2) {
if (!(type & available_error_type_v2))
return -EINVAL;
}
/* /*
* We need extra sanity checks for memory errors. * We need extra sanity checks for memory errors.
* Other types leap directly to injection. * Other types leap directly to injection.
@ -632,6 +747,8 @@ static u64 error_param2;
static u64 error_param3; static u64 error_param3;
static u64 error_param4; static u64 error_param4;
static struct dentry *einj_debug_dir; static struct dentry *einj_debug_dir;
static char einj_buf[32];
static bool einj_v2_enabled;
static struct { u32 mask; const char *str; } const einj_error_type_string[] = { static struct { u32 mask; const char *str; } const einj_error_type_string[] = {
{ BIT(0), "Processor Correctable" }, { BIT(0), "Processor Correctable" },
{ BIT(1), "Processor Uncorrectable non-fatal" }, { BIT(1), "Processor Uncorrectable non-fatal" },
@ -648,6 +765,12 @@ static struct { u32 mask; const char *str; } const einj_error_type_string[] = {
{ BIT(31), "Vendor Defined Error Types" }, { BIT(31), "Vendor Defined Error Types" },
}; };
static struct { u32 mask; const char *str; } const einjv2_error_type_string[] = {
{ BIT(0), "EINJV2 Processor Error" },
{ BIT(1), "EINJV2 Memory Error" },
{ BIT(2), "EINJV2 PCI Express Error" },
};
static int available_error_type_show(struct seq_file *m, void *v) static int available_error_type_show(struct seq_file *m, void *v)
{ {
@ -655,17 +778,22 @@ static int available_error_type_show(struct seq_file *m, void *v)
if (available_error_type & einj_error_type_string[pos].mask) if (available_error_type & einj_error_type_string[pos].mask)
seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask, seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask,
einj_error_type_string[pos].str); einj_error_type_string[pos].str);
if ((available_error_type & ACPI65_EINJV2_SUPP) && einj_v2_enabled) {
for (int pos = 0; pos < ARRAY_SIZE(einjv2_error_type_string); pos++) {
if (available_error_type_v2 & einjv2_error_type_string[pos].mask)
seq_printf(m, "V2_0x%08x\t%s\n", einjv2_error_type_string[pos].mask,
einjv2_error_type_string[pos].str);
}
}
return 0; return 0;
} }
DEFINE_SHOW_ATTRIBUTE(available_error_type); DEFINE_SHOW_ATTRIBUTE(available_error_type);
static int error_type_get(void *data, u64 *val) static ssize_t error_type_get(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{ {
*val = error_type; return simple_read_from_buffer(buf, count, ppos, einj_buf, strlen(einj_buf));
return 0;
} }
bool einj_is_cxl_error_type(u64 type) bool einj_is_cxl_error_type(u64 type)
@ -692,15 +820,35 @@ int einj_validate_error_type(u64 type)
if (tval & (tval - 1)) if (tval & (tval - 1))
return -EINVAL; return -EINVAL;
if (!vendor) if (!vendor)
if (!(type & available_error_type)) if (!(type & (available_error_type | available_error_type_v2)))
return -EINVAL; return -EINVAL;
return 0; return 0;
} }
static int error_type_set(void *data, u64 val) static ssize_t error_type_set(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{ {
int rc; int rc;
u64 val;
/* Leave the last character for the NUL terminator */
if (count > sizeof(einj_buf) - 1)
return -EINVAL;
memset(einj_buf, 0, sizeof(einj_buf));
if (copy_from_user(einj_buf, buf, count))
return -EFAULT;
if (strncmp(einj_buf, "V2_", 3) == 0) {
if (!sscanf(einj_buf, "V2_%llx", &val))
return -EINVAL;
is_v2 = true;
} else {
if (!sscanf(einj_buf, "%llx", &val))
return -EINVAL;
is_v2 = false;
}
rc = einj_validate_error_type(val); rc = einj_validate_error_type(val);
if (rc) if (rc)
@ -708,17 +856,24 @@ static int error_type_set(void *data, u64 val)
error_type = val; error_type = val;
return 0; return count;
} }
DEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set, static const struct file_operations error_type_fops = {
"0x%llx\n"); .read = error_type_get,
.write = error_type_set,
};
static int error_inject_set(void *data, u64 val) static int error_inject_set(void *data, u64 val)
{ {
if (!error_type) if (!error_type)
return -EINVAL; return -EINVAL;
if (is_v2)
error_flags |= SETWA_FLAGS_EINJV2;
else
error_flags &= ~SETWA_FLAGS_EINJV2;
return einj_error_inject(error_type, error_flags, error_param1, error_param2, return einj_error_inject(error_type, error_flags, error_param1, error_param2,
error_param3, error_param4); error_param3, error_param4);
} }
@ -741,6 +896,98 @@ static int einj_check_table(struct acpi_table_einj *einj_tab)
return 0; return 0;
} }
static ssize_t u128_read(struct file *f, char __user *buf, size_t count, loff_t *off)
{
char output[2 * COMPONENT_LEN + 1];
u8 *data = f->f_inode->i_private;
int i;
if (*off >= sizeof(output))
return 0;
for (i = 0; i < COMPONENT_LEN; i++)
sprintf(output + 2 * i, "%.02x", data[COMPONENT_LEN - i - 1]);
output[2 * COMPONENT_LEN] = '\n';
return simple_read_from_buffer(buf, count, off, output, sizeof(output));
}
static ssize_t u128_write(struct file *f, const char __user *buf, size_t count, loff_t *off)
{
char input[2 + 2 * COMPONENT_LEN + 2];
u8 *save = f->f_inode->i_private;
u8 tmp[COMPONENT_LEN];
char byte[3] = {};
char *s, *e;
ssize_t c;
long val;
int i;
/* Require that user supply whole input line in one write(2) syscall */
if (*off)
return -EINVAL;
c = simple_write_to_buffer(input, sizeof(input), off, buf, count);
if (c < 0)
return c;
if (c < 1 || input[c - 1] != '\n')
return -EINVAL;
/* Empty line means invalidate this entry */
if (c == 1) {
memset(save, 0xff, COMPONENT_LEN);
return c;
}
if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X'))
s = input + 2;
else
s = input;
e = input + c - 1;
for (i = 0; i < COMPONENT_LEN; i++) {
byte[1] = *--e;
byte[0] = e > s ? *--e : '0';
if (kstrtol(byte, 16, &val))
return -EINVAL;
tmp[i] = val;
if (e <= s)
break;
}
while (++i < COMPONENT_LEN)
tmp[i] = 0;
memcpy(save, tmp, COMPONENT_LEN);
return c;
}
static const struct file_operations u128_fops = {
.read = u128_read,
.write = u128_write,
};
static bool setup_einjv2_component_files(void)
{
char name[32];
syndrome_data = kcalloc(max_nr_components, sizeof(syndrome_data[0]), GFP_KERNEL);
if (!syndrome_data)
return false;
for (int i = 0; i < max_nr_components; i++) {
sprintf(name, "component_id%d", i);
debugfs_create_file(name, 0600, einj_debug_dir,
&syndrome_data[i].comp_id, &u128_fops);
sprintf(name, "component_syndrome%d", i);
debugfs_create_file(name, 0600, einj_debug_dir,
&syndrome_data[i].comp_synd, &u128_fops);
}
return true;
}
static int __init einj_probe(struct faux_device *fdev) static int __init einj_probe(struct faux_device *fdev)
{ {
int rc; int rc;
@ -764,7 +1011,7 @@ static int __init einj_probe(struct faux_device *fdev)
goto err_put_table; goto err_put_table;
} }
rc = einj_get_available_error_type(&available_error_type); rc = einj_get_available_error_types(&available_error_type, &available_error_type_v2);
if (rc) if (rc)
goto err_put_table; goto err_put_table;
@ -812,6 +1059,8 @@ static int __init einj_probe(struct faux_device *fdev)
&error_param4); &error_param4);
debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR, debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
einj_debug_dir, &notrigger); einj_debug_dir, &notrigger);
if (available_error_type & ACPI65_EINJV2_SUPP)
einj_v2_enabled = setup_einjv2_component_files();
} }
if (vendor_dev[0]) { if (vendor_dev[0]) {
@ -848,7 +1097,7 @@ static void __exit einj_remove(struct faux_device *fdev)
if (einj_param) { if (einj_param) {
acpi_size size = (acpi5) ? acpi_size size = (acpi5) ?
sizeof(struct set_error_type_with_address) : v5param_size :
sizeof(struct einj_parameter); sizeof(struct einj_parameter);
acpi_os_unmap_iomem(einj_param, size); acpi_os_unmap_iomem(einj_param, size);
@ -860,6 +1109,7 @@ static void __exit einj_remove(struct faux_device *fdev)
apei_resources_release(&einj_resources); apei_resources_release(&einj_resources);
apei_resources_fini(&einj_resources); apei_resources_fini(&einj_resources);
debugfs_remove_recursive(einj_debug_dir); debugfs_remove_recursive(einj_debug_dir);
kfree(syndrome_data);
acpi_put_table((struct acpi_table_header *)einj_tab); acpi_put_table((struct acpi_table_header *)einj_tab);
} }

View File

@ -30,7 +30,7 @@ int einj_cxl_available_error_type_show(struct seq_file *m, void *v)
int cxl_err, rc; int cxl_err, rc;
u32 available_error_type = 0; u32 available_error_type = 0;
rc = einj_get_available_error_type(&available_error_type); rc = einj_get_available_error_type(&available_error_type, ACPI_EINJ_GET_ERROR_TYPE);
if (rc) if (rc)
return rc; return rc;

View File

@ -464,28 +464,41 @@ static void ghes_clear_estatus(struct ghes *ghes,
ghes_ack_error(ghes->generic_v2); ghes_ack_error(ghes->generic_v2);
} }
/* /**
* Called as task_work before returning to user-space. * struct ghes_task_work - for synchronous RAS event
* Ensure any queued work has been done before we return to the context that *
* triggered the notification. * @twork: callback_head for task work
* @pfn: page frame number of corrupted page
* @flags: work control flags
*
* Structure to pass task work to be handled before
* returning to user-space via task_work_add().
*/ */
static void ghes_kick_task_work(struct callback_head *head) struct ghes_task_work {
struct callback_head twork;
u64 pfn;
int flags;
};
static void memory_failure_cb(struct callback_head *twork)
{ {
struct acpi_hest_generic_status *estatus; struct ghes_task_work *twcb = container_of(twork, struct ghes_task_work, twork);
struct ghes_estatus_node *estatus_node; int ret;
u32 node_len;
estatus_node = container_of(head, struct ghes_estatus_node, task_work); ret = memory_failure(twcb->pfn, twcb->flags);
if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb));
memory_failure_queue_kick(estatus_node->task_work_cpu);
estatus = GHES_ESTATUS_FROM_NODE(estatus_node); if (!ret || ret == -EHWPOISON || ret == -EOPNOTSUPP)
node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus)); return;
gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len);
pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\n",
twcb->pfn, current->comm, task_pid_nr(current));
force_sig(SIGBUS);
} }
static bool ghes_do_memory_failure(u64 physical_addr, int flags) static bool ghes_do_memory_failure(u64 physical_addr, int flags)
{ {
struct ghes_task_work *twcb;
unsigned long pfn; unsigned long pfn;
if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE))
@ -499,6 +512,18 @@ static bool ghes_do_memory_failure(u64 physical_addr, int flags)
return false; return false;
} }
if (flags == MF_ACTION_REQUIRED && current->mm) {
twcb = (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb));
if (!twcb)
return false;
twcb->pfn = pfn;
twcb->flags = flags;
init_task_work(&twcb->twork, memory_failure_cb);
task_work_add(current, &twcb->twork, TWA_RESUME);
return true;
}
memory_failure_queue(pfn, flags); memory_failure_queue(pfn, flags);
return true; return true;
} }
@ -842,7 +867,7 @@ int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
} }
EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL");
static bool ghes_do_proc(struct ghes *ghes, static void ghes_do_proc(struct ghes *ghes,
const struct acpi_hest_generic_status *estatus) const struct acpi_hest_generic_status *estatus)
{ {
int sev, sec_sev; int sev, sec_sev;
@ -902,7 +927,16 @@ static bool ghes_do_proc(struct ghes *ghes,
} }
} }
return queued; /*
* If no memory failure work is queued for abnormal synchronous
* errors, do a force kill.
*/
if (sync && !queued) {
dev_err(ghes->dev,
HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n",
current->comm, task_pid_nr(current));
force_sig(SIGBUS);
}
} }
static void __ghes_print_estatus(const char *pfx, static void __ghes_print_estatus(const char *pfx,
@ -1088,6 +1122,8 @@ static void __ghes_panic(struct ghes *ghes,
__ghes_print_estatus(KERN_EMERG, ghes->generic, estatus); __ghes_print_estatus(KERN_EMERG, ghes->generic, estatus);
add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK);
ghes_clear_estatus(ghes, estatus, buf_paddr, fixmap_idx); ghes_clear_estatus(ghes, estatus, buf_paddr, fixmap_idx);
if (!panic_timeout) if (!panic_timeout)
@ -1206,9 +1242,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
struct ghes_estatus_node *estatus_node; struct ghes_estatus_node *estatus_node;
struct acpi_hest_generic *generic; struct acpi_hest_generic *generic;
struct acpi_hest_generic_status *estatus; struct acpi_hest_generic_status *estatus;
bool task_work_pending;
u32 len, node_len; u32 len, node_len;
int ret;
llnode = llist_del_all(&ghes_estatus_llist); llnode = llist_del_all(&ghes_estatus_llist);
/* /*
@ -1223,25 +1257,16 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
estatus = GHES_ESTATUS_FROM_NODE(estatus_node); estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
len = cper_estatus_len(estatus); len = cper_estatus_len(estatus);
node_len = GHES_ESTATUS_NODE_LEN(len); node_len = GHES_ESTATUS_NODE_LEN(len);
task_work_pending = ghes_do_proc(estatus_node->ghes, estatus);
ghes_do_proc(estatus_node->ghes, estatus);
if (!ghes_estatus_cached(estatus)) { if (!ghes_estatus_cached(estatus)) {
generic = estatus_node->generic; generic = estatus_node->generic;
if (ghes_print_estatus(NULL, generic, estatus)) if (ghes_print_estatus(NULL, generic, estatus))
ghes_estatus_cache_add(generic, estatus); ghes_estatus_cache_add(generic, estatus);
} }
gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
if (task_work_pending && current->mm) { node_len);
estatus_node->task_work.func = ghes_kick_task_work;
estatus_node->task_work_cpu = smp_processor_id();
ret = task_work_add(current, &estatus_node->task_work,
TWA_RESUME);
if (ret)
estatus_node->task_work.func = NULL;
}
if (!estatus_node->task_work.func)
gen_pool_free(ghes_estatus_pool,
(unsigned long)estatus_node, node_len);
llnode = next; llnode = next;
} }
@ -1302,7 +1327,6 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
estatus_node->ghes = ghes; estatus_node->ghes = ghes;
estatus_node->generic = ghes->generic; estatus_node->generic = ghes->generic;
estatus_node->task_work.func = NULL;
estatus = GHES_ESTATUS_FROM_NODE(estatus_node); estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) { if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) {

View File

@ -1406,7 +1406,7 @@ static int __init acpi_bus_init(void)
goto error1; goto error1;
/* /*
* Register the for all standard device notifications. * Register for all standard device notifications.
*/ */
status = status =
acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY,

View File

@ -1366,6 +1366,8 @@ static int acpi_subsys_poweroff_noirq(struct device *dev)
} }
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static void acpi_dev_pm_detach(struct device *dev, bool power_off);
static struct dev_pm_domain acpi_general_pm_domain = { static struct dev_pm_domain acpi_general_pm_domain = {
.ops = { .ops = {
.runtime_suspend = acpi_subsys_runtime_suspend, .runtime_suspend = acpi_subsys_runtime_suspend,
@ -1386,6 +1388,7 @@ static struct dev_pm_domain acpi_general_pm_domain = {
.restore_early = acpi_subsys_restore_early, .restore_early = acpi_subsys_restore_early,
#endif #endif
}, },
.detach = acpi_dev_pm_detach,
}; };
/** /**
@ -1469,7 +1472,6 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
acpi_device_wakeup_disable(adev); acpi_device_wakeup_disable(adev);
} }
dev->pm_domain->detach = acpi_dev_pm_detach;
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);

View File

@ -238,6 +238,8 @@ static const struct acpi_device_id int3407_device_ids[] = {
{"INTC10A5", 0}, {"INTC10A5", 0},
{"INTC10D8", 0}, {"INTC10D8", 0},
{"INTC10D9", 0}, {"INTC10D9", 0},
{"INTC1100", 0},
{"INTC1101", 0},
{"", 0}, {"", 0},
}; };
MODULE_DEVICE_TABLE(acpi, int3407_device_ids); MODULE_DEVICE_TABLE(acpi, int3407_device_ids);

View File

@ -61,6 +61,13 @@ static const struct acpi_device_id int340x_thermal_device_ids[] = {
{"INTC10D7"}, {"INTC10D7"},
{"INTC10D8"}, {"INTC10D8"},
{"INTC10D9"}, {"INTC10D9"},
{"INTC10FC"},
{"INTC10FD"},
{"INTC10FE"},
{"INTC10FF"},
{"INTC1100"},
{"INTC1101"},
{"INTC1102"},
{""}, {""},
}; };

View File

@ -20,6 +20,7 @@
{"INTC106A", }, /* Fan for Lunar Lake generation */ \ {"INTC106A", }, /* Fan for Lunar Lake generation */ \
{"INTC10A2", }, /* Fan for Raptor Lake generation */ \ {"INTC10A2", }, /* Fan for Raptor Lake generation */ \
{"INTC10D6", }, /* Fan for Panther Lake generation */ \ {"INTC10D6", }, /* Fan for Panther Lake generation */ \
{"INTC10FE", }, /* Fan for Wildcat Lake generation */ \
{"PNP0C0B", } /* Generic ACPI fan */ {"PNP0C0B", } /* Generic ACPI fan */
#define ACPI_FPS_NAME_LEN 20 #define ACPI_FPS_NAME_LEN 20

View File

@ -22,9 +22,9 @@ static ssize_t show_state(struct device *dev, struct device_attribute *attr, cha
int count; int count;
if (fps->control == 0xFFFFFFFF || fps->control > 100) if (fps->control == 0xFFFFFFFF || fps->control > 100)
count = scnprintf(buf, PAGE_SIZE, "not-defined:"); count = sysfs_emit(buf, "not-defined:");
else else
count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); count = sysfs_emit(buf, "%lld:", fps->control);
if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9)
count += sysfs_emit_at(buf, count, "not-defined:"); count += sysfs_emit_at(buf, count, "not-defined:");
@ -59,7 +59,7 @@ static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr,
if (status) if (status)
return status; return status;
return sprintf(buf, "%lld\n", fst.speed); return sysfs_emit(buf, "%lld\n", fst.speed);
} }
static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf)
@ -67,7 +67,7 @@ static ssize_t show_fine_grain_control(struct device *dev, struct device_attribu
struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
struct acpi_fan *fan = acpi_driver_data(acpi_dev); struct acpi_fan *fan = acpi_driver_data(acpi_dev);
return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl); return sysfs_emit(buf, "%d\n", fan->fif.fine_grain_ctrl);
} }
int acpi_fan_create_attributes(struct acpi_device *device) int acpi_fan_create_attributes(struct acpi_device *device)

View File

@ -102,7 +102,7 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
break; break;
} }
if (i == fan->fps_count) { if (i == fan->fps_count) {
dev_dbg(&device->dev, "Invalid control value returned\n"); dev_dbg(&device->dev, "No matching fps control value\n");
return -EINVAL; return -EINVAL;
} }

View File

@ -268,7 +268,7 @@ static int acpi_pci_link_get_current(struct acpi_pci_link *link)
link->irq.active = irq; link->irq.active = irq;
acpi_handle_debug(handle, "Link at IRQ %d \n", link->irq.active); acpi_handle_debug(handle, "Link at IRQ %d\n", link->irq.active);
end: end:
return result; return result;

View File

@ -127,8 +127,11 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
pfru_dev->rev_id, pfru_dev->rev_id,
PFRU_FUNC_QUERY_UPDATE_CAP, PFRU_FUNC_QUERY_UPDATE_CAP,
NULL, ACPI_TYPE_PACKAGE); NULL, ACPI_TYPE_PACKAGE);
if (!out_obj) if (!out_obj) {
dev_dbg(pfru_dev->parent_dev,
"Query cap failed with no object\n");
return ret; return ret;
}
if (out_obj->package.count < CAP_NR_IDX || if (out_obj->package.count < CAP_NR_IDX ||
out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
@ -141,13 +144,17 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
out_obj->package.elements[CAP_DRV_SVN_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[CAP_DRV_SVN_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[CAP_PLAT_ID_IDX].type != ACPI_TYPE_BUFFER || out_obj->package.elements[CAP_PLAT_ID_IDX].type != ACPI_TYPE_BUFFER ||
out_obj->package.elements[CAP_OEM_ID_IDX].type != ACPI_TYPE_BUFFER || out_obj->package.elements[CAP_OEM_ID_IDX].type != ACPI_TYPE_BUFFER ||
out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) {
dev_dbg(pfru_dev->parent_dev,
"Query cap failed with invalid package count/type\n");
goto free_acpi_buffer; goto free_acpi_buffer;
}
cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value; cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value;
if (cap_hdr->status != DSM_SUCCEED) { if (cap_hdr->status != DSM_SUCCEED) {
ret = -EBUSY; ret = -EBUSY;
dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", cap_hdr->status); dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n",
cap_hdr->status);
goto free_acpi_buffer; goto free_acpi_buffer;
} }
@ -193,24 +200,32 @@ static int query_buffer(struct pfru_com_buf_info *info,
out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF, pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF,
NULL, ACPI_TYPE_PACKAGE); NULL, ACPI_TYPE_PACKAGE);
if (!out_obj) if (!out_obj) {
dev_dbg(pfru_dev->parent_dev,
"Query buf failed with no object\n");
return ret; return ret;
}
if (out_obj->package.count < BUF_NR_IDX || if (out_obj->package.count < BUF_NR_IDX ||
out_obj->package.elements[BUF_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[BUF_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[BUF_ADDR_LOW_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_ADDR_LOW_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[BUF_ADDR_HI_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_ADDR_HI_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) {
dev_dbg(pfru_dev->parent_dev,
"Query buf failed with invalid package count/type\n");
goto free_acpi_buffer; goto free_acpi_buffer;
}
info->status = out_obj->package.elements[BUF_STATUS_IDX].integer.value; info->status = out_obj->package.elements[BUF_STATUS_IDX].integer.value;
info->ext_status = info->ext_status =
out_obj->package.elements[BUF_EXT_STATUS_IDX].integer.value; out_obj->package.elements[BUF_EXT_STATUS_IDX].integer.value;
if (info->status != DSM_SUCCEED) { if (info->status != DSM_SUCCEED) {
ret = -EBUSY; ret = -EBUSY;
dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", info->status); dev_dbg(pfru_dev->parent_dev,
dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", info->ext_status); "Query buf failed with Error Status:%d\n", info->status);
dev_dbg(pfru_dev->parent_dev,
"Query buf failed with Error Extended Status:%d\n", info->ext_status);
goto free_acpi_buffer; goto free_acpi_buffer;
} }
@ -295,12 +310,16 @@ static bool applicable_image(const void *data, struct pfru_update_cap_info *cap,
m_img_hdr = data + size; m_img_hdr = data + size;
type = get_image_type(m_img_hdr, pfru_dev); type = get_image_type(m_img_hdr, pfru_dev);
if (type < 0) if (type < 0) {
dev_dbg(pfru_dev->parent_dev, "Invalid image type\n");
return false; return false;
}
size = adjust_efi_size(m_img_hdr, size); size = adjust_efi_size(m_img_hdr, size);
if (size < 0) if (size < 0) {
dev_dbg(pfru_dev->parent_dev, "Invalid image size\n");
return false; return false;
}
auth = data + size; auth = data + size;
size += sizeof(u64) + auth->auth_info.hdr.len; size += sizeof(u64) + auth->auth_info.hdr.len;
@ -346,8 +365,11 @@ static int start_update(int action, struct pfru_device *pfru_dev)
out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
pfru_dev->rev_id, PFRU_FUNC_START, pfru_dev->rev_id, PFRU_FUNC_START,
&in_obj, ACPI_TYPE_PACKAGE); &in_obj, ACPI_TYPE_PACKAGE);
if (!out_obj) if (!out_obj) {
dev_dbg(pfru_dev->parent_dev,
"Update failed to start with no object\n");
return ret; return ret;
}
if (out_obj->package.count < UPDATE_NR_IDX || if (out_obj->package.count < UPDATE_NR_IDX ||
out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER ||
@ -355,8 +377,11 @@ static int start_update(int action, struct pfru_device *pfru_dev)
out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER ||
out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) {
dev_dbg(pfru_dev->parent_dev,
"Update failed with invalid package count/type\n");
goto free_acpi_buffer; goto free_acpi_buffer;
}
update_result.status = update_result.status =
out_obj->package.elements[UPDATE_STATUS_IDX].integer.value; out_obj->package.elements[UPDATE_STATUS_IDX].integer.value;
@ -365,8 +390,10 @@ static int start_update(int action, struct pfru_device *pfru_dev)
if (update_result.status != DSM_SUCCEED) { if (update_result.status != DSM_SUCCEED) {
ret = -EBUSY; ret = -EBUSY;
dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", update_result.status); dev_dbg(pfru_dev->parent_dev,
dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", "Update failed with Error Status:%d\n", update_result.status);
dev_dbg(pfru_dev->parent_dev,
"Update failed with Error Extended Status:%d\n",
update_result.ext_status); update_result.ext_status);
goto free_acpi_buffer; goto free_acpi_buffer;
@ -450,8 +477,10 @@ static ssize_t pfru_write(struct file *file, const char __user *buf,
if (ret) if (ret)
return ret; return ret;
if (len > buf_info.buf_size) if (len > buf_info.buf_size) {
dev_dbg(pfru_dev->parent_dev, "Capsule image size too large\n");
return -EINVAL; return -EINVAL;
}
iov.iov_base = (void __user *)buf; iov.iov_base = (void __user *)buf;
iov.iov_len = len; iov.iov_len = len;
@ -460,10 +489,14 @@ static ssize_t pfru_write(struct file *file, const char __user *buf,
/* map the communication buffer */ /* map the communication buffer */
phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo); phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo);
buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB); buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB);
if (!buf_ptr) if (!buf_ptr) {
dev_dbg(pfru_dev->parent_dev, "Failed to remap the buffer\n");
return -ENOMEM; return -ENOMEM;
}
if (!copy_from_iter_full(buf_ptr, len, &iter)) { if (!copy_from_iter_full(buf_ptr, len, &iter)) {
dev_dbg(pfru_dev->parent_dev,
"Failed to copy the data from the user space buffer\n");
ret = -EINVAL; ret = -EINVAL;
goto unmap; goto unmap;
} }

View File

@ -85,8 +85,6 @@ static u64 efi_pa_va_lookup(efi_guid_t *guid, u64 pa)
} }
} }
pr_warn("Failed to find VA for GUID: %pUL, PA: 0x%llx", guid, pa);
return 0; return 0;
} }
@ -154,13 +152,37 @@ acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end)
guid_copy(&th->guid, (guid_t *)handler_info->handler_guid); guid_copy(&th->guid, (guid_t *)handler_info->handler_guid);
th->handler_addr = th->handler_addr =
(void *)efi_pa_va_lookup(&th->guid, handler_info->handler_address); (void *)efi_pa_va_lookup(&th->guid, handler_info->handler_address);
/*
* Print a warning message if handler_addr is zero which is not expected to
* ever happen.
*/
if (unlikely(!th->handler_addr))
pr_warn("Failed to find VA of handler for GUID: %pUL, PA: 0x%llx",
&th->guid, handler_info->handler_address);
th->static_data_buffer_addr = th->static_data_buffer_addr =
efi_pa_va_lookup(&th->guid, handler_info->static_data_buffer_address); efi_pa_va_lookup(&th->guid, handler_info->static_data_buffer_address);
/*
* According to the PRM specification, static_data_buffer_address can be zero,
* so avoid printing a warning message in that case. Otherwise, if the
* return value of efi_pa_va_lookup() is zero, print the message.
*/
if (unlikely(!th->static_data_buffer_addr && handler_info->static_data_buffer_address))
pr_warn("Failed to find VA of static data buffer for GUID: %pUL, PA: 0x%llx",
&th->guid, handler_info->static_data_buffer_address);
th->acpi_param_buffer_addr = th->acpi_param_buffer_addr =
efi_pa_va_lookup(&th->guid, handler_info->acpi_param_buffer_address); efi_pa_va_lookup(&th->guid, handler_info->acpi_param_buffer_address);
/*
* According to the PRM specification, acpi_param_buffer_address can be zero,
* so avoid printing a warning message in that case. Otherwise, if the
* return value of efi_pa_va_lookup() is zero, print the message.
*/
if (unlikely(!th->acpi_param_buffer_addr && handler_info->acpi_param_buffer_address))
pr_warn("Failed to find VA of acpi param buffer for GUID: %pUL, PA: 0x%llx",
&th->guid, handler_info->acpi_param_buffer_address);
} while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info))); } while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info)));
return 0; return 0;

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/export.h> #include <linux/string_choices.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/acpi.h> #include <linux/acpi.h>
@ -30,17 +30,16 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
if (!dev->wakeup.flags.valid) if (!dev->wakeup.flags.valid)
continue; continue;
seq_printf(seq, "%s\t S%d\t", seq_printf(seq, "%s\t S%llu\t",
dev->pnp.bus_id, dev->pnp.bus_id,
(u32) dev->wakeup.sleep_state); dev->wakeup.sleep_state);
mutex_lock(&dev->physical_node_lock); mutex_lock(&dev->physical_node_lock);
if (!dev->physical_node_count) { if (!dev->physical_node_count) {
seq_printf(seq, "%c%-8s\n", seq_printf(seq, "%c%-8s\n",
dev->wakeup.flags.valid ? '*' : ' ', dev->wakeup.flags.valid ? '*' : ' ',
device_may_wakeup(&dev->dev) ? str_enabled_disabled(device_may_wakeup(&dev->dev)));
"enabled" : "disabled");
} else { } else {
struct device *ldev; struct device *ldev;
list_for_each_entry(entry, &dev->physical_node_list, list_for_each_entry(entry, &dev->physical_node_list,
@ -55,9 +54,8 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
seq_printf(seq, "%c%-8s %s:%s\n", seq_printf(seq, "%c%-8s %s:%s\n",
dev->wakeup.flags.valid ? '*' : ' ', dev->wakeup.flags.valid ? '*' : ' ',
(device_may_wakeup(&dev->dev) || str_enabled_disabled(device_may_wakeup(ldev) ||
device_may_wakeup(ldev)) ? device_may_wakeup(&dev->dev)),
"enabled" : "disabled",
ldev->bus ? ldev->bus->name : ldev->bus ? ldev->bus->name :
"no-bus", dev_name(ldev)); "no-bus", dev_name(ldev));
put_device(ldev); put_device(ldev);
@ -141,6 +139,5 @@ static const struct proc_ops acpi_system_wakeup_device_proc_ops = {
void __init acpi_sleep_proc_init(void) void __init acpi_sleep_proc_init(void)
{ {
/* 'wakeup device' [R/W] */ /* 'wakeup device' [R/W] */
proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, proc_create("wakeup", 0644, acpi_root_dir, &acpi_system_wakeup_device_proc_ops);
acpi_root_dir, &acpi_system_wakeup_device_proc_ops);
} }

View File

@ -173,11 +173,14 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy)
{ {
unsigned int cpu; unsigned int cpu;
if (ignore_ppc == 1)
return;
for_each_cpu(cpu, policy->related_cpus) { for_each_cpu(cpu, policy->related_cpus) {
struct acpi_processor *pr = per_cpu(processors, cpu); struct acpi_processor *pr = per_cpu(processors, cpu);
int ret; int ret;
if (!pr) if (!pr || !pr->performance)
continue; continue;
/* /*
@ -193,6 +196,11 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy)
if (ret < 0) if (ret < 0)
pr_err("Failed to add freq constraint for CPU%d (%d)\n", pr_err("Failed to add freq constraint for CPU%d (%d)\n",
cpu, ret); cpu, ret);
ret = acpi_processor_get_platform_limit(pr);
if (ret)
pr_err("Failed to update freq constraint for CPU%d (%d)\n",
cpu, ret);
} }
} }

View File

@ -235,7 +235,7 @@ static int acpi_processor_throttling_notifier(unsigned long event, void *data)
if (pr->throttling_platform_limit > target_state) if (pr->throttling_platform_limit > target_state)
target_state = pr->throttling_platform_limit; target_state = pr->throttling_platform_limit;
if (target_state >= p_throttling->state_count) { if (target_state >= p_throttling->state_count) {
pr_warn("Exceed the limit of T-state \n"); pr_warn("Exceed the limit of T-state\n");
target_state = p_throttling->state_count - 1; target_state = p_throttling->state_count - 1;
} }
p_tstate->target_state = target_state; p_tstate->target_state = target_state;

View File

@ -42,7 +42,7 @@ void acpi_enable_wakeup_devices(u8 sleep_state)
list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
wakeup_list) { wakeup_list) {
if (!dev->wakeup.flags.valid if (!dev->wakeup.flags.valid
|| sleep_state > (u32) dev->wakeup.sleep_state || sleep_state > dev->wakeup.sleep_state
|| !(device_may_wakeup(&dev->dev) || !(device_may_wakeup(&dev->dev)
|| dev->wakeup.prepare_count)) || dev->wakeup.prepare_count))
continue; continue;
@ -67,7 +67,7 @@ void acpi_disable_wakeup_devices(u8 sleep_state)
list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
wakeup_list) { wakeup_list) {
if (!dev->wakeup.flags.valid if (!dev->wakeup.flags.valid
|| sleep_state > (u32) dev->wakeup.sleep_state || sleep_state > dev->wakeup.sleep_state
|| !(device_may_wakeup(&dev->dev) || !(device_may_wakeup(&dev->dev)
|| dev->wakeup.prepare_count)) || dev->wakeup.prepare_count))
continue; continue;

View File

@ -387,9 +387,6 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
{ "INT3435", LPSS_ADDR(lpt_uart_dev_desc) }, { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) },
{ "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) }, { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
/* Wildcat Point LPSS devices */
{ "INT3438", LPSS_ADDR(lpt_spi_dev_desc) },
{ } { }
}; };

View File

@ -690,6 +690,7 @@ static const struct acpi_device_id int3400_thermal_match[] = {
{"INTC1068", 0}, {"INTC1068", 0},
{"INTC10A0", 0}, {"INTC10A0", 0},
{"INTC10D4", 0}, {"INTC10D4", 0},
{"INTC10FC", 0},
{} {}
}; };

View File

@ -276,6 +276,7 @@ static const struct acpi_device_id int3403_device_ids[] = {
{"INTC1069", 0}, {"INTC1069", 0},
{"INTC10A1", 0}, {"INTC10A1", 0},
{"INTC10D5", 0}, {"INTC10D5", 0},
{"INTC10FD", 0},
{"", 0}, {"", 0},
}; };
MODULE_DEVICE_TABLE(acpi, int3403_device_ids); MODULE_DEVICE_TABLE(acpi, int3403_device_ids);

View File

@ -35,9 +35,6 @@ struct ghes_estatus_node {
struct llist_node llnode; struct llist_node llnode;
struct acpi_hest_generic *generic; struct acpi_hest_generic *generic;
struct ghes *ghes; struct ghes *ghes;
int task_work_cpu;
struct callback_head task_work;
}; };
struct ghes_estatus_cache { struct ghes_estatus_cache {

View File

@ -3901,7 +3901,6 @@ enum mf_flags {
int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index, int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
unsigned long count, int mf_flags); unsigned long count, int mf_flags);
extern int memory_failure(unsigned long pfn, int flags); extern int memory_failure(unsigned long pfn, int flags);
extern void memory_failure_queue_kick(int cpu);
extern int unpoison_memory(unsigned long pfn); extern int unpoison_memory(unsigned long pfn);
extern atomic_long_t num_poisoned_pages __read_mostly; extern atomic_long_t num_poisoned_pages __read_mostly;
extern int soft_offline_page(unsigned long pfn, int flags); extern int soft_offline_page(unsigned long pfn, int flags);

View File

@ -2507,19 +2507,6 @@ static void memory_failure_work_func(struct work_struct *work)
} }
} }
/*
* Process memory_failure work queued on the specified CPU.
* Used to avoid return-to-userspace racing with the memory_failure workqueue.
*/
void memory_failure_queue_kick(int cpu)
{
struct memory_failure_cpu *mf_cpu;
mf_cpu = &per_cpu(memory_failure_cpu, cpu);
cancel_work_sync(&mf_cpu->work);
memory_failure_work_func(&mf_cpu->work);
}
static int __init memory_failure_init(void) static int __init memory_failure_init(void)
{ {
struct memory_failure_cpu *mf_cpu; struct memory_failure_cpu *mf_cpu;