aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/osl.c
diff options
context:
space:
mode:
authorThomas Renninger <trenn@suse.de>2008-02-05 02:31:22 -0500
committerLen Brown <len.brown@intel.com>2008-02-07 00:59:18 -0500
commitdf92e695998e1bc6e426a840eb86d6d1ee87e2a5 (patch)
treea131aefb2a66e2dc32e533652a5471ccbc2211e0 /drivers/acpi/osl.c
parent488b5ec871191359b9b79262a3d48456dae7ea5f (diff)
ACPI: track opregion names to avoid driver resource conflicts.
Small ACPICA extension to be able to store the name of operation regions in osl.c later In ACPI, AML can define accesses to IO ports and System Memory by Operation Regions. Those are not registered as done by PNPACPI using resource templates (and _CRS/_SRS methods). The IO ports and System Memory regions may get accessed by arbitrary AML code. When native drivers are accessing the same resources bad things can happen (e.g. a critical shutdown temperature of 3000 C every 2 months or so). It is not really possible to register the operation regions via request_resource, as they often overlap with pnp or other resources (e.g. statically setup IO resources below 0x100). This approach stores all Operation Region declarations (IO and System Memory only) at ACPI table parse time. It offers a similar functionality like request_region and let drivers which are known to possibly use the same IO ports and Memory which are also often used by ACPI (hwmon and i2c) check for ACPI interference. A boot parameter acpi_enforce_resources=strict/lax/no is provided, which is default set to lax: - strict: let conflicting drivers fail to load with an error message - lax: let conflicting driver work normal with a warning message - no: no functional change at all Depending on the feedback and the kind of interferences we see, this should be set to strict at later time. Goal of this patch set is: - Identify ACPI interferences in bug reports (very hard to reproduce and to identify) - Find BIOSes for that an ACPI driver should exist for specific HW instead of a native one. - stability in general Provide acpi_check_{mem_}region. Drivers can additionally check against possible ACPI interference by also invoking this shortly before they call request_region. If -EBUSY is returned, the driver must not load. Use acpi_enforce_resources=strict/lax/no options to: - strict: let conflicting drivers fail to load with an error message - lax: let conflicting driver work normal with a warning message - no: no functional change at all Cc: "Mark M. Hoffman" <mhoffman@lightlink.com> Cc: Jean Delvare <khali@linux-fr.org> Cc: Len Brown <lenb@kernel.org> Cc: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Thomas Renninger <trenn@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r--drivers/acpi/osl.c175
1 files changed, 173 insertions, 2 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index e53fb516f9d..222f7b1b66f 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -44,6 +44,8 @@
44#include <asm/uaccess.h> 44#include <asm/uaccess.h>
45 45
46#include <linux/efi.h> 46#include <linux/efi.h>
47#include <linux/ioport.h>
48#include <linux/list.h>
47 49
48#define _COMPONENT ACPI_OS_SERVICES 50#define _COMPONENT ACPI_OS_SERVICES
49ACPI_MODULE_NAME("osl"); 51ACPI_MODULE_NAME("osl");
@@ -74,6 +76,18 @@ static void *acpi_irq_context;
74static struct workqueue_struct *kacpid_wq; 76static struct workqueue_struct *kacpid_wq;
75static struct workqueue_struct *kacpi_notify_wq; 77static struct workqueue_struct *kacpi_notify_wq;
76 78
79struct acpi_res_list {
80 resource_size_t start;
81 resource_size_t end;
82 acpi_adr_space_type resource_type; /* IO port, System memory, ...*/
83 char name[5]; /* only can have a length of 4 chars, make use of this
84 one instead of res->name, no need to kalloc then */
85 struct list_head resource_list;
86};
87
88static LIST_HEAD(resource_list_head);
89static DEFINE_SPINLOCK(acpi_res_lock);
90
77#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ 91#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */
78static char osi_additional_string[OSI_STRING_LENGTH_MAX]; 92static char osi_additional_string[OSI_STRING_LENGTH_MAX];
79 93
@@ -1102,6 +1116,127 @@ static int __init acpi_wake_gpes_always_on_setup(char *str)
1102 1116
1103__setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); 1117__setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup);
1104 1118
1119/* Check of resource interference between native drivers and ACPI
1120 * OperationRegions (SystemIO and System Memory only).
1121 * IO ports and memory declared in ACPI might be used by the ACPI subsystem
1122 * in arbitrary AML code and can interfere with legacy drivers.
1123 * acpi_enforce_resources= can be set to:
1124 *
1125 * - strict (2)
1126 * -> further driver trying to access the resources will not load
1127 * - lax (default) (1)
1128 * -> further driver trying to access the resources will load, but you
1129 * get a system message that something might go wrong...
1130 *
1131 * - no (0)
1132 * -> ACPI Operation Region resources will not be registered
1133 *
1134 */
1135#define ENFORCE_RESOURCES_STRICT 2
1136#define ENFORCE_RESOURCES_LAX 1
1137#define ENFORCE_RESOURCES_NO 0
1138
1139static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
1140
1141static int __init acpi_enforce_resources_setup(char *str)
1142{
1143 if (str == NULL || *str == '\0')
1144 return 0;
1145
1146 if (!strcmp("strict", str))
1147 acpi_enforce_resources = ENFORCE_RESOURCES_STRICT;
1148 else if (!strcmp("lax", str))
1149 acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
1150 else if (!strcmp("no", str))
1151 acpi_enforce_resources = ENFORCE_RESOURCES_NO;
1152
1153 return 1;
1154}
1155
1156__setup("acpi_enforce_resources=", acpi_enforce_resources_setup);
1157
1158/* Check for resource conflicts between ACPI OperationRegions and native
1159 * drivers */
1160static int acpi_check_resource_conflict(struct resource *res)
1161{
1162 struct acpi_res_list *res_list_elem;
1163 int ioport;
1164 int clash = 0;
1165
1166 if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
1167 return 0;
1168 if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM))
1169 return 0;
1170
1171 ioport = res->flags & IORESOURCE_IO;
1172
1173 spin_lock(&acpi_res_lock);
1174 list_for_each_entry(res_list_elem, &resource_list_head,
1175 resource_list) {
1176 if (ioport && (res_list_elem->resource_type
1177 != ACPI_ADR_SPACE_SYSTEM_IO))
1178 continue;
1179 if (!ioport && (res_list_elem->resource_type
1180 != ACPI_ADR_SPACE_SYSTEM_MEMORY))
1181 continue;
1182
1183 if (res->end < res_list_elem->start
1184 || res_list_elem->end < res->start)
1185 continue;
1186 clash = 1;
1187 break;
1188 }
1189 spin_unlock(&acpi_res_lock);
1190
1191 if (clash) {
1192 if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) {
1193 printk(KERN_INFO "%sACPI: %s resource %s [0x%llx-0x%llx]"
1194 " conflicts with ACPI region %s"
1195 " [0x%llx-0x%llx]\n",
1196 acpi_enforce_resources == ENFORCE_RESOURCES_LAX
1197 ? KERN_WARNING : KERN_ERR,
1198 ioport ? "I/O" : "Memory", res->name,
1199 (long long) res->start, (long long) res->end,
1200 res_list_elem->name,
1201 (long long) res_list_elem->start,
1202 (long long) res_list_elem->end);
1203 printk(KERN_INFO "ACPI: Device needs an ACPI driver\n");
1204 }
1205 if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT)
1206 return -EBUSY;
1207 }
1208 return 0;
1209}
1210
1211int acpi_check_region(resource_size_t start, resource_size_t n,
1212 const char *name)
1213{
1214 struct resource res = {
1215 .start = start,
1216 .end = start + n - 1,
1217 .name = name,
1218 .flags = IORESOURCE_IO,
1219 };
1220
1221 return acpi_check_resource_conflict(&res);
1222}
1223EXPORT_SYMBOL(acpi_check_region);
1224
1225int acpi_check_mem_region(resource_size_t start, resource_size_t n,
1226 const char *name)
1227{
1228 struct resource res = {
1229 .start = start,
1230 .end = start + n - 1,
1231 .name = name,
1232 .flags = IORESOURCE_MEM,
1233 };
1234
1235 return acpi_check_resource_conflict(&res);
1236
1237}
1238EXPORT_SYMBOL(acpi_check_mem_region);
1239
1105/* 1240/*
1106 * Acquire a spinlock. 1241 * Acquire a spinlock.
1107 * 1242 *
@@ -1303,10 +1438,46 @@ acpi_status
1303acpi_os_validate_address ( 1438acpi_os_validate_address (
1304 u8 space_id, 1439 u8 space_id,
1305 acpi_physical_address address, 1440 acpi_physical_address address,
1306 acpi_size length) 1441 acpi_size length,
1442 char *name)
1307{ 1443{
1444 struct acpi_res_list *res;
1445 if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
1446 return AE_OK;
1308 1447
1309 return AE_OK; 1448 switch (space_id) {
1449 case ACPI_ADR_SPACE_SYSTEM_IO:
1450 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
1451 /* Only interference checks against SystemIO and SytemMemory
1452 are needed */
1453 res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL);
1454 if (!res)
1455 return AE_OK;
1456 /* ACPI names are fixed to 4 bytes, still better use strlcpy */
1457 strlcpy(res->name, name, 5);
1458 res->start = address;
1459 res->end = address + length - 1;
1460 res->resource_type = space_id;
1461 spin_lock(&acpi_res_lock);
1462 list_add(&res->resource_list, &resource_list_head);
1463 spin_unlock(&acpi_res_lock);
1464 pr_debug("Added %s resource: start: 0x%llx, end: 0x%llx, "
1465 "name: %s\n", (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
1466 ? "SystemIO" : "System Memory",
1467 (unsigned long long)res->start,
1468 (unsigned long long)res->end,
1469 res->name);
1470 break;
1471 case ACPI_ADR_SPACE_PCI_CONFIG:
1472 case ACPI_ADR_SPACE_EC:
1473 case ACPI_ADR_SPACE_SMBUS:
1474 case ACPI_ADR_SPACE_CMOS:
1475 case ACPI_ADR_SPACE_PCI_BAR_TARGET:
1476 case ACPI_ADR_SPACE_DATA_TABLE:
1477 case ACPI_ADR_SPACE_FIXED_HARDWARE:
1478 break;
1479 }
1480 return AE_OK;
1310} 1481}
1311 1482
1312#endif 1483#endif