[PATCH 2/2] acpi: bus: Introduce acpi.skip_ids= boot parameter

[email protected]
Newsgroups org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Denis Mukhin <[email protected]> 

In virtualized environments it is possible that a device managed by the
hypervisor or another VM is described in the ACPI tables. In these
cases, binding a device driver to it can cause functional issues.

To resolve device ownership conflicts, skip enumeration of ACPI devices
whose ACPI IDs match entries specified via a new boot-time parameter:

    acpi.skip_ids=<HID[:UID]>[,<HID[:UID]>...]

This parameter may also be used when a single kernel image must support
both bare-metal and virtualized environments - for example, when the
image is shared across multiple VMs and only selected platform devices
are exposed through passthrough.

Note, a single driver may support multiple devices and only a subset of
those devices may require exclusion (for example, ABCD0020:00 but not
ABCD0020:01). As a result, existing 'initcall_blacklist=' is not an
appropriate solution to resolve the ownership conflict.

Add brief explanation for apci.skip_ids= in the kernel command line
documentation.

Signed-off-by: Denis Mukhin <[email protected]>
---
 .../admin-guide/kernel-parameters.txt         |  5 ++
 drivers/acpi/bus.c                            | 60 +++++++++++++++++--
 2 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 3d35270dddef..a2d4bd9e37b9 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -195,6 +195,11 @@ Kernel parameters
 			ACPI firmware problems, as the system might behave erratically
 			after having encountered a fatal ACPI error.
 
+	acpi.skip_ids=	[ACPI] Skip enumeration of ACPI devices whose HID[:UID]
+			matches an entry in the given comma-separated list
+			(up to 16 entries).
+			Format: HID[:UID][,HID[:UID]...]
+
 	acpi_enforce_resources=	[ACPI]
 			{ strict | lax | no }
 			Check for resource conflicts between native drivers
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a30a904f6535..86dba970bfc2 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -38,6 +38,15 @@ struct acpi_device *acpi_root;
 struct proc_dir_entry *acpi_root_dir;
 EXPORT_SYMBOL(acpi_root_dir);
 
+#define ACPI_MAX_SKIP_IDS	16 /* Arbitrary limit. */
+
+static char *acpi_skip_ids[ACPI_MAX_SKIP_IDS];
+static int acpi_skip_num;
+module_param_array_named(skip_ids, acpi_skip_ids, charp,
+		&acpi_skip_num, 0444);
+MODULE_PARM_DESC(skip_ids,
+		"Skip binding ACPI drivers to devices with matching _HID[:_UID]");
+
 #ifdef CONFIG_X86
 #ifdef CONFIG_ACPI_CUSTOM_DSDT
 static inline int set_copy_dsdt(const struct dmi_system_id *id)
@@ -1008,6 +1017,9 @@ static bool __acpi_match_device(const struct acpi_device *device,
 {
 	const struct acpi_device_id *id;
 	struct acpi_hardware_id *hwid;
+	struct acpi_device_info *info = NULL;
+	bool ret = false;
+	int i;
 
 	/*
 	 * If the device is not present, it is unnecessary to load device
@@ -1016,8 +1028,42 @@ static bool __acpi_match_device(const struct acpi_device *device,
 	if (!device || !device->status.present)
 		return false;
 
+	if (acpi_skip_num) {
+		acpi_status status;
+
+		status = acpi_get_object_info(device->handle, &info);
+		if (ACPI_FAILURE(status))
+			info = NULL;
+	}
+
 	list_for_each_entry(hwid, &device->pnp.ids, list) {
-		/* First, check the ACPI/PNP IDs provided by the caller. */
+		/* First, check whether device ACPI ID is in the skip list. */
+		for (i = 0; i < acpi_skip_num; i++) {
+			char with_uid[MAX_ACPI_DEVICE_NAME_LEN];
+			u32 uid;
+
+			if (!strcasecmp(acpi_skip_ids[i], hwid->id)) {
+				ret = false;
+				goto out;
+			}
+
+			if (!info || !(info->valid & ACPI_VALID_UID))
+				continue;
+
+			if (kstrtou32(info->unique_id.string, 0, &uid))
+				snprintf(with_uid, sizeof(with_uid), "%s:%s",
+					 hwid->id, info->unique_id.string);
+			else
+				snprintf(with_uid, sizeof(with_uid), "%s:%02x",
+					 hwid->id, uid);
+
+			if (!strcasecmp(acpi_skip_ids[i], with_uid)) {
+				ret = false;
+				goto out;
+			}
+		}
+
+		/* Second, check the ACPI/PNP IDs provided by the caller. */
 		if (acpi_ids) {
 			for (id = acpi_ids; id->id[0] || id->cls; id++) {
 				if (id->id[0] && !strcmp((char *)id->id, hwid->id))
@@ -1031,12 +1077,18 @@ static bool __acpi_match_device(const struct acpi_device *device,
 		 * Next, check ACPI_DT_NAMESPACE_HID and try to match the
 		 * "compatible" property if found.
 		 */
-		if (!strcmp(ACPI_DT_NAMESPACE_HID, hwid->id))
-			return acpi_of_match_device(device, of_ids, of_id);
+		if (!strcmp(ACPI_DT_NAMESPACE_HID, hwid->id)) {
+			ret = acpi_of_match_device(device, of_ids, of_id);
+			goto out;
+		}
 	}
-	return false;
+
+out:
+	kfree(info);
+	return ret;
 
 out_acpi_match:
+	kfree(info);
 	if (acpi_id)
 		*acpi_id = id;
 	return true;
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.