Merge branches 'release' and 'hwmon-conflicts' into release
Len Brown [Thu, 7 Feb 2008 08:31:17 +0000 (03:31 -0500)]
drivers/acpi/dispatcher/dsopcode.c
drivers/acpi/osl.c
include/acpi/acpiosxf.h
include/linux/acpi.h

index fc9da48..f501e08 100644 (file)
@@ -359,7 +359,9 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc)
 
        status = acpi_os_validate_address(obj_desc->region.space_id,
                                          obj_desc->region.address,
-                                         (acpi_size) obj_desc->region.length);
+                                         (acpi_size) obj_desc->region.length,
+                                         acpi_ut_get_node_name(node));
+
        if (ACPI_FAILURE(status)) {
                /*
                 * Invalid address/length. We will emit an error message and mark
index 3b8aef3..6f49f64 100644 (file)
@@ -44,6 +44,8 @@
 #include <asm/uaccess.h>
 
 #include <linux/efi.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
 
 #define _COMPONENT             ACPI_OS_SERVICES
 ACPI_MODULE_NAME("osl");
@@ -74,6 +76,18 @@ static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
 static struct workqueue_struct *kacpi_notify_wq;
 
+struct acpi_res_list {
+       resource_size_t start;
+       resource_size_t end;
+       acpi_adr_space_type resource_type; /* IO port, System memory, ...*/
+       char name[5];   /* only can have a length of 4 chars, make use of this
+                          one instead of res->name, no need to kalloc then */
+       struct list_head resource_list;
+};
+
+static LIST_HEAD(resource_list_head);
+static DEFINE_SPINLOCK(acpi_res_lock);
+
 #define        OSI_STRING_LENGTH_MAX 64        /* arbitrary */
 static char osi_additional_string[OSI_STRING_LENGTH_MAX];
 
@@ -1091,6 +1105,128 @@ static int __init acpi_wake_gpes_always_on_setup(char *str)
 
 __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup);
 
+/* Check of resource interference between native drivers and ACPI
+ * OperationRegions (SystemIO and System Memory only).
+ * IO ports and memory declared in ACPI might be used by the ACPI subsystem
+ * in arbitrary AML code and can interfere with legacy drivers.
+ * acpi_enforce_resources= can be set to:
+ *
+ *   - strict           (2)
+ *     -> further driver trying to access the resources will not load
+ *   - lax (default)    (1)
+ *     -> further driver trying to access the resources will load, but you
+ *     get a system message that something might go wrong...
+ *
+ *   - no               (0)
+ *     -> ACPI Operation Region resources will not be registered
+ *
+ */
+#define ENFORCE_RESOURCES_STRICT 2
+#define ENFORCE_RESOURCES_LAX    1
+#define ENFORCE_RESOURCES_NO     0
+
+static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
+
+static int __init acpi_enforce_resources_setup(char *str)
+{
+       if (str == NULL || *str == '\0')
+               return 0;
+
+       if (!strcmp("strict", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_STRICT;
+       else if (!strcmp("lax", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_LAX;
+       else if (!strcmp("no", str))
+               acpi_enforce_resources = ENFORCE_RESOURCES_NO;
+
+       return 1;
+}
+
+__setup("acpi_enforce_resources=", acpi_enforce_resources_setup);
+
+/* Check for resource conflicts between ACPI OperationRegions and native
+ * drivers */
+int acpi_check_resource_conflict(struct resource *res)
+{
+       struct acpi_res_list *res_list_elem;
+       int ioport;
+       int clash = 0;
+
+       if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
+               return 0;
+       if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM))
+               return 0;
+
+       ioport = res->flags & IORESOURCE_IO;
+
+       spin_lock(&acpi_res_lock);
+       list_for_each_entry(res_list_elem, &resource_list_head,
+                           resource_list) {
+               if (ioport && (res_list_elem->resource_type
+                              != ACPI_ADR_SPACE_SYSTEM_IO))
+                       continue;
+               if (!ioport && (res_list_elem->resource_type
+                               != ACPI_ADR_SPACE_SYSTEM_MEMORY))
+                       continue;
+
+               if (res->end < res_list_elem->start
+                   || res_list_elem->end < res->start)
+                       continue;
+               clash = 1;
+               break;
+       }
+       spin_unlock(&acpi_res_lock);
+
+       if (clash) {
+               if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) {
+                       printk(KERN_INFO "%sACPI: %s resource %s [0x%llx-0x%llx]"
+                              " conflicts with ACPI region %s"
+                              " [0x%llx-0x%llx]\n",
+                              acpi_enforce_resources == ENFORCE_RESOURCES_LAX
+                              ? KERN_WARNING : KERN_ERR,
+                              ioport ? "I/O" : "Memory", res->name,
+                              (long long) res->start, (long long) res->end,
+                              res_list_elem->name,
+                              (long long) res_list_elem->start,
+                              (long long) res_list_elem->end);
+                       printk(KERN_INFO "ACPI: Device needs an ACPI driver\n");
+               }
+               if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT)
+                       return -EBUSY;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(acpi_check_resource_conflict);
+
+int acpi_check_region(resource_size_t start, resource_size_t n,
+                     const char *name)
+{
+       struct resource res = {
+               .start = start,
+               .end   = start + n - 1,
+               .name  = name,
+               .flags = IORESOURCE_IO,
+       };
+
+       return acpi_check_resource_conflict(&res);
+}
+EXPORT_SYMBOL(acpi_check_region);
+
+int acpi_check_mem_region(resource_size_t start, resource_size_t n,
+                     const char *name)
+{
+       struct resource res = {
+               .start = start,
+               .end   = start + n - 1,
+               .name  = name,
+               .flags = IORESOURCE_MEM,
+       };
+
+       return acpi_check_resource_conflict(&res);
+
+}
+EXPORT_SYMBOL(acpi_check_mem_region);
+
 /*
  * Acquire a spinlock.
  *
@@ -1292,10 +1428,46 @@ acpi_status
 acpi_os_validate_address (
     u8                   space_id,
     acpi_physical_address   address,
-    acpi_size               length)
+    acpi_size               length,
+    char *name)
 {
+       struct acpi_res_list *res;
+       if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
+               return AE_OK;
 
-    return AE_OK;
+       switch (space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               /* Only interference checks against SystemIO and SytemMemory
+                  are needed */
+               res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL);
+               if (!res)
+                       return AE_OK;
+               /* ACPI names are fixed to 4 bytes, still better use strlcpy */
+               strlcpy(res->name, name, 5);
+               res->start = address;
+               res->end = address + length - 1;
+               res->resource_type = space_id;
+               spin_lock(&acpi_res_lock);
+               list_add(&res->resource_list, &resource_list_head);
+               spin_unlock(&acpi_res_lock);
+               pr_debug("Added %s resource: start: 0x%llx, end: 0x%llx, "
+                        "name: %s\n", (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+                        ? "SystemIO" : "System Memory",
+                        (unsigned long long)res->start,
+                        (unsigned long long)res->end,
+                        res->name);
+               break;
+       case ACPI_ADR_SPACE_PCI_CONFIG:
+       case ACPI_ADR_SPACE_EC:
+       case ACPI_ADR_SPACE_SMBUS:
+       case ACPI_ADR_SPACE_CMOS:
+       case ACPI_ADR_SPACE_PCI_BAR_TARGET:
+       case ACPI_ADR_SPACE_DATA_TABLE:
+       case ACPI_ADR_SPACE_FIXED_HARDWARE:
+               break;
+       }
+       return AE_OK;
 }
 
 #endif
index 1a16cfb..022a5fd 100644 (file)
@@ -242,8 +242,8 @@ acpi_status acpi_os_validate_interface(char *interface);
 acpi_status acpi_osi_invalidate(char* interface);
 
 acpi_status
-acpi_os_validate_address(u8 space_id,
-                        acpi_physical_address address, acpi_size length);
+acpi_os_validate_address(u8 space_id, acpi_physical_address address,
+                        acpi_size length, char *name);
 
 u64 acpi_os_get_timer(void);
 
index 5a5a13b..25fb4dc 100644 (file)
@@ -25,6 +25,7 @@
 #ifndef _LINUX_ACPI_H
 #define _LINUX_ACPI_H
 
+#include <linux/ioport.h>      /* for struct resource */
 
 #ifdef CONFIG_ACPI
 
@@ -238,6 +239,13 @@ extern int pnpacpi_disabled;
 #define PXM_INVAL      (-1)
 #define NID_INVAL      (-1)
 
+int acpi_check_resource_conflict(struct resource *res);
+
+int acpi_check_region(resource_size_t start, resource_size_t n,
+                     const char *name);
+int acpi_check_mem_region(resource_size_t start, resource_size_t n,
+                     const char *name);
+
 #else  /* CONFIG_ACPI */
 
 static inline int acpi_boot_init(void)
@@ -250,5 +258,22 @@ static inline int acpi_boot_table_init(void)
        return 0;
 }
 
+static inline int acpi_check_resource_conflict(struct resource *res)
+{
+       return 0;
+}
+
+static inline int acpi_check_region(resource_size_t start, resource_size_t n,
+                                   const char *name)
+{
+       return 0;
+}
+
+static inline int acpi_check_mem_region(resource_size_t start,
+                                       resource_size_t n, const char *name)
+{
+       return 0;
+}
+
 #endif /* !CONFIG_ACPI */
 #endif /*_LINUX_ACPI_H*/