Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[RFC PATCH 02/10] acpi: install SSDT tables from initrd

41 views
Skip to first unread message

Octavian Purdila

unread,
Mar 31, 2016, 5:50:06 AM3/31/16
to
This patch allows loading user defined SSDTs from the first,
uncompressed, initrd. The SSDT aml code must be stored in files under
the /kernel/firmware/acpi/overlay path.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
Documentation/acpi/ssdt-overlays.txt | 94 ++++++++++++++++++++++++++++++++++++
drivers/acpi/bus.c | 63 ++++++++++++++++++++++++
2 files changed, 157 insertions(+)
create mode 100644 Documentation/acpi/ssdt-overlays.txt

diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
new file mode 100644
index 0000000..a94c3f9
--- /dev/null
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -0,0 +1,94 @@
+
+In order to support ACPI open-ended hardware configurations (e.g. development
+boards) we need a way to augment the ACPI configuration provided by the firmware
+image. A common example is connecting sensors on I2C / SPI buses on development
+boards.
+
+Although this can be accomplished by creating a kernel platform driver or
+recompiling the firmware image with updated ACPI tables, neither is practical:
+the former proliferates board specific kernel code while the latter requires
+access to firmware tools which are often not publicly available.
+
+Because ACPI supports external references in AML code a more practical
+way to augment firmware ACPI configuration is by dynamically loading
+user defined SSDT tables that contain the board specific information.
+
+For example, to enumerate a Bosch BMA222E accelerometer on the I2C bus of the
+Minnowboard MAX development board exposed via the LSE connector [1], the
+following ASL code can be used:
+
+DefinitionBlock ("minnowmax.aml", "SSDT", 1, "Vendor", "Accel", 0x00000003)
+{
+ External (\_SB.I2C6, DeviceObj)
+
+ Scope (\_SB.I2C6)
+ {
+ Device (STAC)
+ {
+ Name (_ADR, Zero)
+ Name (_HID, "BMA222E")
+
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RBUF, ResourceTemplate ()
+ {
+ I2cSerialBus (0x0018, ControllerInitiated, 0x00061A80,
+ AddressingMode7Bit, "\\_SB.I2C6", 0x00,
+ ResourceConsumer, ,)
+ GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000,
+ "\\_SB.GPO2", 0x00, ResourceConsumer, , )
+ { // Pin list
+ 0
+ }
+ })
+ Return (RBUF)
+ }
+ }
+ }
+}
+
+which can then be compiled to AML binary format:
+
+$ iasl minnowmax.asl
+
+Intel ACPI Component Architecture
+ASL Optimizing Compiler version 20140214-64 [Mar 29 2014]
+Copyright (c) 2000 - 2014 Intel Corporation
+
+ASL Input: minnomax.asl - 30 lines, 614 bytes, 7 keywords
+AML Output: minnowmax.aml - 165 bytes, 6 named objects, 1 executable opcodes
+
+[1] http://wiki.minnowboard.org/MinnowBoard_MAX#Low_Speed_Expansion_Connector_.28Top.29
+
+The resulting AML code can then be loaded by the kernel using one of the methods
+below.
+
+== Loading ACPI SSDTs from initrd ==
+
+This option allows loading of user defined SSDTs from initrd and it is useful
+when the system does not support EFI or when there is not enough EFI storage.
+
+It works in a similar way with initrd based ACPI tables overrides: SSDT aml code
+must be placed in the first, uncompressed, initrd under the
+"kernel/firmware/acpi/overlay" path. We use a different path than the initrd
+tables override to avoid conflicts with the override feature.
+
+Multiple files can be used and this will translate in loading multiple
+tables. Only tables with the SSDT signature will be loaded.
+
+Here is an example:
+
+# Add the raw ACPI tables to an uncompressed cpio archive.
+# They must be put into a /kernel/firmware/acpi/overlay directory inside the
+# cpio archive.
+# The uncompressed cpio archive must be the first.
+# Other, typically compressed cpio archives, must be
+# concatenated on top of the uncompressed one.
+mkdir -p kernel/firmware/acpi
+cp ssdt.aml kernel/firmware/acpi
+
+# Create the uncompressed cpio archive and concatenate the original initrd
+# on top:
+find kernel | cpio -H newc --create > /boot/instrumented_initrd
+cat /boot/initrd >>/boot/instrumented_initrd
+
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 891c42d..5e0d076 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,9 +37,14 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
+#include <linux/initrd.h>
+#include <linux/earlycpio.h>

#include "internal.h"

+#undef pr_fmt
+#define pr_fmt(fmt) "ACPI: " fmt
+
#define _COMPONENT ACPI_BUS_COMPONENT
ACPI_MODULE_NAME("bus");

@@ -863,6 +868,62 @@ static int __init acpi_bus_init_irq(void)
return 0;
}

+void __init acpi_load_initrd_ssdts(void)
+{
+ void *data = (void *)initrd_start;
+ int size = initrd_end - initrd_start;
+ const char *path = "kernel/firmware/acpi/overlay";
+ long offset = 0;
+ struct cpio_data file;
+ struct acpi_table_header *header;
+ void *table;
+ acpi_status status;
+
+ while (true) {
+ file = find_cpio_data(path, data, size, &offset);
+ if (!file.data)
+ break;
+
+ data += offset;
+ size -= offset;
+
+ if (file.size < sizeof(struct acpi_table_header)) {
+ pr_err("initrd table smaller than ACPI header [%s%s]\n",
+ path, file.name);
+ continue;
+ }
+
+ header = file.data;
+
+ if (file.size != header->length) {
+ pr_err("initrd file / table length mismatch [%s%s]\n",
+ path, file.name);
+ continue;
+ }
+
+ if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) {
+ pr_warn("skipping non-SSDT initrd table [%s%s]\n",
+ path, file.name);
+ continue;
+ }
+
+ table = kmemdup(file.data, file.size, GFP_KERNEL);
+ if (!table)
+ continue;
+
+ status = acpi_install_table((uintptr_t)table, 0);
+ if (ACPI_FAILURE(status)) {
+ pr_err("failed to install SSDT from initrd [%s%s]\n",
+ path, file.name);
+ kfree(table);
+ }
+
+ pr_info("installed SSDT table found in initrd [%s%s][0x%x]\n",
+ path, file.name, header->length);
+ add_taint(TAINT_OVERLAY_ACPI_TABLE, LOCKDEP_STILL_OK);
+ }
+}
+
/**
* acpi_early_init - Initialize ACPICA and populate the ACPI namespace.
*
@@ -911,6 +972,8 @@ void __init acpi_early_init(void)
goto error0;
}

+ acpi_load_initrd_ssdts();
+
status = acpi_load_tables();
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX
--
1.9.1

Zheng, Lv

unread,
Apr 1, 2016, 1:10:06 AM4/1/16
to
Hi,

IMO, there is already a similar function upstreamed:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c85cc81
Could it work for your use case?
[Lv Zheng]
I can see that this is so similar to the acpi_initrd_initialize_tables() which is in the drivers/acpi/osl.c.
Please check.

Thanks and best regards
-Lv

> /**
> * acpi_early_init - Initialize ACPICA and populate the ACPI namespace.
> *
> @@ -911,6 +972,8 @@ void __init acpi_early_init(void)
> goto error0;
> }
>
> + acpi_load_initrd_ssdts();
> +
> status = acpi_load_tables();
> if (ACPI_FAILURE(status)) {
> printk(KERN_ERR PREFIX
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
> the body of a message to majo...@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html

Octavian Purdila

unread,
Apr 1, 2016, 6:20:06 AM4/1/16
to
On Fri, Apr 1, 2016 at 8:05 AM, Zheng, Lv <lv.z...@intel.com> wrote:
> Hi,
>
> IMO, there is already a similar function upstreamed:
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c85cc81
> Could it work for your use case?

Yes, it is basically the same.

The only difference is on how we handle taint. I think we should use a
new taint for overlays and that we don't need to disable lockdep.

BTW, why is lockdep disabled when we override?

Octavian Purdila

unread,
Apr 4, 2016, 9:10:08 AM4/4/16
to
The other thing I forgot to mention is that I think we should allow
installing new tables even if CONFIG_ACPI_INITRD_TABLE_OVERRIDE is not
selected. IMO the override and overlay functionality is different,
with the latter being more then a debug option.

I will prepare a patch for the next version of the series to decouple
installing new tables from CONFIG_ACPI_INITRD_TABLE_OVERRIDE.

Zheng, Lv

unread,
Apr 4, 2016, 9:00:07 PM4/4/16
to
Hi,

> From: Octavian Purdila [mailto:octavian...@intel.com]
> Subject: Re: [RFC PATCH 02/10] acpi: install SSDT tables from initrd
>
> On Fri, Apr 1, 2016 at 1:11 PM, Octavian Purdila
> <octavian...@intel.com> wrote:
> > On Fri, Apr 1, 2016 at 8:05 AM, Zheng, Lv <lv.z...@intel.com> wrote:
> >> Hi,
> >>
> >> IMO, there is already a similar function upstreamed:
> >>
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c85c
> c81
> >> Could it work for your use case?
> >
> > Yes, it is basically the same.
> >
> > The only difference is on how we handle taint. I think we should use a
> > new taint for overlays and that we don't need to disable lockdep.
> >
> > BTW, why is lockdep disabled when we override?
>
> The other thing I forgot to mention is that I think we should allow
> installing new tables even if CONFIG_ACPI_INITRD_TABLE_OVERRIDE is not
> selected. IMO the override and overlay functionality is different,
> with the latter being more then a debug option.
[Lv Zheng]
I don't think so. The initrd override mechanism is not dependent on CONFIG_ACPI_DEBUG.
According to the spec, we can allow a higher versioning same table (same table sig, table id, table oem id) to override the old tables as a patching functionality.
So both the functionalities are not debug options and serve for the same purpose from this point of view.
And IMO that's why the initrd override mechanism needn't be dependent on CONFIG_ACPI_DEBUG.

I'm really OK with removing the acpi_table_taint() for CONFIG_ACPI_INITRD_TABLE_OVERRIDE but leaving some info messages indicating the table upgrades.
I don't think this mechanism is unsafe.
It happens during a initialization step occurring before the table is loaded and hence should be safe even the synchronization is not so robust in ACPICA.
And with the revision support added, we should be able to allow vendors to update the buggy tables.
That means the tables may be originated from the safe sources - the vendors.

>
> I will prepare a patch for the next version of the series to decouple
> installing new tables from CONFIG_ACPI_INITRD_TABLE_OVERRIDE.
[Lv Zheng]
I don't think they need to be decoupled.
The use case is:
If there is an ACPI table in initrd image and:
1. if the table's revision is higher than the existing one, override the existing one;
2. if the table is a brand new one, install it.

Zheng, Lv

unread,
Apr 4, 2016, 9:00:09 PM4/4/16
to
Hi,

> From: linux-ac...@vger.kernel.org [mailto:linux-acpi-
> ow...@vger.kernel.org] On Behalf Of Octavian Purdila
[Lv Zheng]
I guess this is because of the old synchronization bugs.
Originally, the table handler may receive table events when the table is installed.
And that may trigger lock issues in such an early stage.

I don't think the acpi_table_taint() need to be there now.
The override mechanisms now happen in an initialization step before the tables are loaded.
It should be safe even the synchronization is not so robust in ACPICA.
Because during this step, all things are serial.
IMO, you can remove acpi_table_taint().

Thanks and best regards
-Lv


Octavian Purdila

unread,
Apr 5, 2016, 3:30:07 AM4/5/16
to
The problem is that CONFIG_ACPI_INITRD_TABLE_OVERRIDE is presented as
a debug option in Documentation/initrd_table_override.txt and most
distributions are not selecting it which makes it hard to use it in
practice.

> I'm really OK with removing the acpi_table_taint() for CONFIG_ACPI_INITRD_TABLE_OVERRIDE but leaving some info messages indicating the table upgrades.
> I don't think this mechanism is unsafe.
> It happens during a initialization step occurring before the table is loaded and hence should be safe even the synchronization is not so robust in ACPICA.
> And with the revision support added, we should be able to allow vendors to update the buggy tables.
> That means the tables may be originated from the safe sources - the vendors.
>
>>
>> I will prepare a patch for the next version of the series to decouple
>> installing new tables from CONFIG_ACPI_INITRD_TABLE_OVERRIDE.
> [Lv Zheng]
> I don't think they need to be decoupled.
> The use case is:
> If there is an ACPI table in initrd image and:
> 1. if the table's revision is higher than the existing one, override the existing one;
> 2. if the table is a brand new one, install it.
>

The implementation will stay the same of course, I was just suggesting
to move CONFIG_ACPI_INITRD_TABLE_OVERRIDE in
acpi_os_physical_table_override to allow new tables to be loaded even
if the option is not selected.

Zheng, Lv

unread,
Apr 6, 2016, 2:20:07 AM4/6/16
to
Hi,

> From: linux-ac...@vger.kernel.org [mailto:linux-acpi-
> ow...@vger.kernel.org] On Behalf Of Octavian Purdila
[Lv Zheng]
This sounds reasonable.
Or you can rename it to CONFIG_ACPI_TABLE_UPGRADE and make it default 'y'.
Also you need to remove acpi_table_taint() which is not so useful now.
0 new messages