Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1368110 > unrolled thread
| Started by | Octavian Purdila <octavian.purdila@intel.com> |
|---|---|
| First post | 2016-03-31 11:40 +0200 |
| Last post | 2016-03-31 11:50 +0200 |
| Articles | 7 — 2 participants |
Back to article view | Back to linux.kernel
[RFC PATCH 00/10] ACPI overlays Octavian Purdila <octavian.purdila@intel.com> - 2016-03-31 11:40 +0200
[RFC PATCH 08/10] configfs: fix CONFIGFS_BIN_ATTR_[RW]O definitions Octavian Purdila <octavian.purdila@intel.com> - 2016-03-31 11:40 +0200
[RFC PATCH 02/10] acpi: install SSDT tables from initrd Octavian Purdila <octavian.purdila@intel.com> - 2016-03-31 11:50 +0200
RE: [RFC PATCH 02/10] acpi: install SSDT tables from initrd "Zheng, Lv" <lv.zheng@intel.com> - 2016-04-01 07:10 +0200
Re: [RFC PATCH 02/10] acpi: install SSDT tables from initrd Octavian Purdila <octavian.purdila@intel.com> - 2016-04-01 12:20 +0200
[RFC PATCH 05/10] i2c: add support for ACPI reconfigure notifications Octavian Purdila <octavian.purdila@intel.com> - 2016-03-31 11:50 +0200
[RFC PATCH 07/10] efi: load SSTDs from EFI variables Octavian Purdila <octavian.purdila@intel.com> - 2016-03-31 11:50 +0200
| From | Octavian Purdila <octavian.purdila@intel.com> |
|---|---|
| Date | 2016-03-31 11:40 +0200 |
| Subject | [RFC PATCH 00/10] ACPI overlays |
| Message-ID | <riHf4-2VE-13@gated-at.bofh.it> |
This patch set enables custom ACPI board configuration by adding mechanisms in the Linux kernel for loading user defined SSDTs. In order to support ACPI open-ended hardware configurations 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. This patch sets provides three methods for loading custom SSDTs: * From an EFI variable This is the preferred method, when EFI is supported on the platform, because it allows a persistent, OS independent way of storing and updating the user defined SSDTs. There is also work underway to implement EFI support for loading user defined SSDTs and using this method will make it easier to convert to the EFI loading mechanism when that will arrive. * From the first uncompressed initrd (similar with the override functionality) This is useful when EFI is not supported on the platform and when it is not possible to defer the loading to userspace. * From userspace via configfs This is useful when we want to defer the operation to userspace for platform detection, loading the SSDTs from a custom partition, etc. Octavian Purdila (10): kernel: add TAINT_OVERLAY_ACPI_TABLE acpi: install SSDT tables from initrd acpi: add support for ACPI reconfiguration notifiers acpi: fix enumeration (visited) flags for bus rescans i2c: add support for ACPI reconfigure notifications spi: add support for ACPI reconfigure notifications efi: load SSTDs from EFI variables configfs: fix CONFIGFS_BIN_ATTR_[RW]O definitions acpi: add support for configfs acpi: add support for loading SSDTs via configfs Documentation/ABI/testing/configfs-acpi | 23 +++++ Documentation/acpi/ssdt-overlays.txt | 174 ++++++++++++++++++++++++++++++++ Documentation/kernel-parameters.txt | 7 ++ Documentation/oops-tracing.txt | 2 + Documentation/sysctl/kernel.txt | 1 + MAINTAINERS | 1 + drivers/acpi/Kconfig | 9 ++ drivers/acpi/Makefile | 1 + drivers/acpi/bus.c | 72 +++++++++++++ drivers/acpi/configfs.c | 143 ++++++++++++++++++++++++++ drivers/acpi/internal.h | 3 + drivers/acpi/scan.c | 73 +++++++++++++- drivers/acpi/sysfs.c | 6 +- drivers/firmware/efi/efi.c | 107 ++++++++++++++++++++ drivers/i2c/i2c-core.c | 38 ++++++- drivers/spi/spi.c | 36 ++++++- include/acpi/acpi_bus.h | 8 ++ include/linux/configfs.h | 4 +- include/linux/kernel.h | 1 + kernel/panic.c | 2 + 20 files changed, 699 insertions(+), 12 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-acpi create mode 100644 Documentation/acpi/ssdt-overlays.txt create mode 100644 drivers/acpi/configfs.c -- 1.9.1
[toc] | [next] | [standalone]
| From | Octavian Purdila <octavian.purdila@intel.com> |
|---|---|
| Date | 2016-03-31 11:40 +0200 |
| Subject | [RFC PATCH 08/10] configfs: fix CONFIGFS_BIN_ATTR_[RW]O definitions |
| Message-ID | <riHf6-2VE-53@gated-at.bofh.it> |
| In reply to | #1368110 |
The type should be struct configfs_bin_attribute and not struct
configfs_attribute.
Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
---
include/linux/configfs.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index f8165c1..ada0613 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -181,7 +181,7 @@ static struct configfs_bin_attribute _pfx##attr_##_name = { \
}
#define CONFIGFS_BIN_ATTR_RO(_pfx, _name, _priv, _maxsz) \
-static struct configfs_attribute _pfx##attr_##_name = { \
+static struct configfs_bin_attribute _pfx##attr_##_name = { \
.cb_attr = { \
.ca_name = __stringify(_name), \
.ca_mode = S_IRUGO, \
@@ -193,7 +193,7 @@ static struct configfs_attribute _pfx##attr_##_name = { \
}
#define CONFIGFS_BIN_ATTR_WO(_pfx, _name, _priv, _maxsz) \
-static struct configfs_attribute _pfx##attr_##_name = { \
+static struct configfs_bin_attribute _pfx##attr_##_name = { \
.cb_attr = { \
.ca_name = __stringify(_name), \
.ca_mode = S_IWUSR, \
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Octavian Purdila <octavian.purdila@intel.com> |
|---|---|
| Date | 2016-03-31 11:50 +0200 |
| Subject | [RFC PATCH 02/10] acpi: install SSDT tables from initrd |
| Message-ID | <riHoK-2Z1-7@gated-at.bofh.it> |
| In reply to | #1368110 |
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.purdila@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
[toc] | [prev] | [next] | [standalone]
| From | "Zheng, Lv" <lv.zheng@intel.com> |
|---|---|
| Date | 2016-04-01 07:10 +0200 |
| Subject | RE: [RFC PATCH 02/10] acpi: install SSDT tables from initrd |
| Message-ID | <riZvk-84e-3@gated-at.bofh.it> |
| In reply to | #1368115 |
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?
> From: linux-acpi-owner@vger.kernel.org [mailto:linux-acpi-
> owner@vger.kernel.org] On Behalf Of Octavian Purdila
> Subject: [RFC PATCH 02/10] acpi: install SSDT tables from initrd
>
> 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.purdila@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_Co
> nnector_.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);
> + }
> +}
> +
[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 majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
[toc] | [prev] | [next] | [standalone]
| From | Octavian Purdila <octavian.purdila@intel.com> |
|---|---|
| Date | 2016-04-01 12:20 +0200 |
| Subject | Re: [RFC PATCH 02/10] acpi: install SSDT tables from initrd |
| Message-ID | <rj4lk-2Pw-11@gated-at.bofh.it> |
| In reply to | #1369003 |
On Fri, Apr 1, 2016 at 8:05 AM, Zheng, Lv <lv.zheng@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?
[toc] | [prev] | [next] | [standalone]
| From | Octavian Purdila <octavian.purdila@intel.com> |
|---|---|
| Date | 2016-03-31 11:50 +0200 |
| Subject | [RFC PATCH 05/10] i2c: add support for ACPI reconfigure notifications |
| Message-ID | <riHoK-2Z1-23@gated-at.bofh.it> |
| In reply to | #1368110 |
This allows the i2c core to enumerate devices from ACPI tables that
are dynamically loaded after the i2c adapter has been probed.
Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
---
drivers/i2c/i2c-core.c | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index ffe715d..7a6f741 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -150,7 +150,8 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
- if (acpi_bus_get_status(adev) || !adev->status.present)
+ if (acpi_bus_get_status(adev) || !adev->status.present ||
+ acpi_device_enumerated(adev))
return AE_OK;
memset(&info, 0, sizeof(info));
@@ -189,6 +190,9 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
adev->power.flags.ignore_parent = true;
strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
+
+ adev->flags.visited = true;
+
if (!i2c_new_device(adapter, &info)) {
adev->power.flags.ignore_parent = false;
dev_err(&adapter->dev,
@@ -224,8 +228,36 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)
dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
}
+static int acpi_i2c_table_load(struct device *dev, void *data)
+{
+ struct i2c_adapter *adapter = i2c_verify_adapter(dev);
+
+ if (!adapter)
+ return 0;
+
+ acpi_i2c_register_devices(adapter);
+ return 0;
+}
+
+static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value,
+ void *arg)
+{
+ switch (value) {
+ case ACPI_RECONFIG_TABLE_LOAD:
+ bus_find_device(&i2c_bus_type, NULL, NULL,
+ acpi_i2c_table_load);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block i2c_acpi_notifier = {
+ .notifier_call = acpi_i2c_notify,
+};
#else /* CONFIG_ACPI */
static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { }
+extern struct notifier_block i2c_acpi_notifier;
#endif /* CONFIG_ACPI */
#ifdef CONFIG_ACPI_I2C_OPREGION
@@ -2117,6 +2149,8 @@ static int __init i2c_init(void)
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
+ if (IS_ENABLED(CONFIG_ACPI))
+ WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
return 0;
@@ -2131,6 +2165,8 @@ bus_err:
static void __exit i2c_exit(void)
{
+ if (IS_ENABLED(CONFIG_ACPI))
+ WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier));
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
i2c_del_driver(&dummy_driver);
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Octavian Purdila <octavian.purdila@intel.com> |
|---|---|
| Date | 2016-03-31 11:50 +0200 |
| Subject | [RFC PATCH 07/10] efi: load SSTDs from EFI variables |
| Message-ID | <riHoK-2Z1-29@gated-at.bofh.it> |
| In reply to | #1368110 |
This patch allows SSDTs to be loaded from EFI variables. It works by
specifying the EFI variable name containing the SSDT to be loaded. All
variables with the same name (regardless of the vendor GUID) will be
loaded.
Note that we can't use acpi_install_table and we must rely on the
dynamic ACPI table loading and bus re-scanning mechanisms. That is
because I2C/SPI controllers are initialized earlier then the EFI
subsystems and all I2C/SPI ACPI devices are enumerated when the
I2C/SPI controllers are initialized.
Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
---
Documentation/acpi/ssdt-overlays.txt | 66 +++++++++++++++++++++
Documentation/kernel-parameters.txt | 7 +++
drivers/firmware/efi/efi.c | 107 +++++++++++++++++++++++++++++++++++
3 files changed, 180 insertions(+)
diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
index a94c3f9..7c588be 100644
--- a/Documentation/acpi/ssdt-overlays.txt
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -92,3 +92,69 @@ cp ssdt.aml kernel/firmware/acpi
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >>/boot/instrumented_initrd
+== Loading ACPI SSDTs from EFI variables ==
+
+This is the preferred method, when EFI is supported on the platform, because it
+allows a persistent, OS independent way of storing the user defined SSDTs. There
+is also work underway to implement EFI support for loading user defined SSDTs
+and using this method will make it easier to convert to the EFI loading
+mechanism when that will arrive.
+
+In order to load SSDTs from an EFI variable the efivar_ssdt kernel command line
+parameter can be used. The argument for the option is the variable name to
+use. If there are multiple variables with the same name but with different
+vendor GUIDs, all of them will be loaded.
+
+In order to store the AML code in an EFI variable the efivarfs filesystem can be
+used. It is enabled and mounted by default in /sys/firmware/efi/efivars in all
+recent distribution.
+
+Creating a new file in /sys/firmware/efi/efivars will automatically create a new
+EFI variable. Updating a file in /sys/firmware/efi/efivars will update the EFI
+variable. Please note that the file name needs to be specially formatted as
+"Name-GUID" and that the first 4 bytes in the file (little-endian format)
+represent the attributes of the EFI variable (see EFI_VARIABLE_MASK in
+include/linux/efi.h). Writing to the file must also be done with one write
+operation.
+
+For example, you can use the following bash script to create/update an EFI
+variable with the content from a given file:
+
+#!/bin/sh -e
+
+while ! [ -z "$1" ]; do
+ case "$1" in
+ "-f") filename="$2"; shift;;
+ "-g") guid="$2"; shift;;
+ *) name="$1";;
+ esac
+ shift
+done
+
+if [ -z "$name" ] || [ -z "$filename" ]; then
+ echo "Syntax: ${0##*/} -f filename [ -g guid ] name"
+ exit 1
+fi
+
+EFIVARFS='/sys/firmware/efi/efivars'
+
+[ -d "$EFIVARFS" ] || exit 2
+
+if stat -tf $EFIVARFS | grep -q -v de5e81e4; then
+ mount -t efivarfs none $EFIVARFS
+fi
+
+# try to pick up an existing GUID
+if [ -z "$guid" ]; then
+ guid=$(find "$EFIVARFS" -name "$name-*" | head -n1 | cut -f2- -d-)
+fi
+
+# use a randomly generated GUID
+if [ -z "$guid" ]; then
+ guid="$(cat /proc/sys/kernel/random/uuid)"
+fi
+
+tmp=$(mktemp)
+/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
+dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
+rm $tmp
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 9a53c92..fe89cda 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1144,6 +1144,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Address Range Mirroring feature even if your box
doesn't support it.
+ efivar_ssdt= [EFI; X86] Name of an EFI variable that contains an SSDT
+ that is to be dynamically loaded by Linux. If there are
+ multiple variables with the same name but with different
+ vendor GUIDs, all of them will be loaded. See
+ Documentation/acpi/ssdt-overlays.txt for details.
+
+
eisa_irq_edge= [PARISC,HW]
See header of drivers/parisc/eisa.c.
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 2cd37da..dda4778 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -24,6 +24,8 @@
#include <linux/of_fdt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
#include <asm/early_ioremap.h>
@@ -193,6 +195,108 @@ static void generic_ops_unregister(void)
efivars_unregister(&generic_efivars);
}
+#if IS_ENABLED(CONFIG_ACPI)
+#define EFIVAR_SSDT_NAME_MAX 16
+static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX];
+static int __init efivar_ssdt_setup(char *str)
+{
+ if (strlen(str) < sizeof(efivar_ssdt))
+ memcpy(efivar_ssdt, str, strlen(str));
+ else
+ pr_warn("efivar_ssdt: name too long: %s\n", str);
+ return 0;
+}
+__setup("efivar_ssdt=", efivar_ssdt_setup);
+
+static LIST_HEAD(efivar_ssdts);
+
+static inline void pr_efivar_name(efi_char16_t *name16)
+{
+ char name[EFIVAR_SSDT_NAME_MAX];
+ int i;
+
+ for (i = 0; i < EFIVAR_SSDT_NAME_MAX - 1; i++)
+ name[i] = name16[i] & 0xFF;
+ name[i] = 0;
+ pr_cont("%s", name);
+}
+
+static __init int efivar_acpi_iter(efi_char16_t *name, efi_guid_t vendor,
+ unsigned long name_size, void *data)
+{
+ int i;
+ int str_len = name_size / sizeof(efi_char16_t);
+ struct efivar_entry *entry;
+
+ if (str_len != strlen(efivar_ssdt) + 1)
+ return 0;
+
+ for (i = 0; i < str_len; i++)
+ if ((name[i] & 0xFF) != efivar_ssdt[i])
+ return 0;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ memcpy(entry->var.VariableName, name, name_size);
+ memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t));
+
+ efivar_entry_add(entry, &efivar_ssdts);
+
+ return 0;
+}
+
+static __init int efivar_ssdt_load(void)
+{
+ struct efivar_entry *i;
+ int err;
+
+ err = efivar_init(efivar_acpi_iter, NULL, false, false,
+ &efivar_ssdts);
+ if (err) {
+ pr_err("%s: efivar_init failed: %d\n", __func__, err);
+ return err;
+ }
+
+ list_for_each_entry(i, &efivar_ssdts, list) {
+ void *data;
+ unsigned long size;
+
+ pr_info("loading SSDT from EFI variable ");
+ pr_efivar_name(i->var.VariableName); pr_cont("\n");
+
+ err = efivar_entry_size(i, &size);
+ if (err) {
+ pr_err("failed to get size\n");
+ continue;
+ }
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (!data)
+ continue;
+
+ err = efivar_entry_get(i, NULL, &size, data);
+ if (err) {
+ pr_err("failed to get data\n");
+ kfree(data);
+ continue;
+ }
+
+ err = acpi_load_table(data);
+ if (err) {
+ pr_err("failed to load table: %d\n", err);
+ kfree(data);
+ continue;
+ }
+
+ add_taint(TAINT_OVERLAY_ACPI_TABLE, LOCKDEP_STILL_OK);
+ }
+
+ return 0;
+}
+#endif
+
/*
* We register the efi subsystem with the firmware subsystem and the
* efivars subsystem with the efi subsystem, if the system was booted with
@@ -216,6 +320,9 @@ static int __init efisubsys_init(void)
if (error)
goto err_put;
+ if (IS_ENABLED(CONFIG_ACPI))
+ efivar_ssdt_load();
+
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
if (error) {
pr_err("efi: Sysfs attribute export failed with error %d.\n",
--
1.9.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web