X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Facpi%2Fosl.c;h=a498a6cc68fec1d463786fba1d3b6c84a6e6b68f;hb=87ebd06f2f362acc3fd866f28a917b53c0ff560a;hp=971eca4864fab3223bc68d97cd032a56d428f4ea;hpb=8800c0ebf5e8363dab66647512dcef5b83203dec;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 971eca4..a498a6c 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -4,6 +4,8 @@ * Copyright (C) 2000 Andrew Henroid * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (c) 2008 Intel Corporation + * Author: Matthew Wilcox * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -30,20 +32,25 @@ #include #include #include -#include #include #include #include +#include #include #include #include -#include +#include +#include +#include +#include +#include + #include -#include -#include #include -#include +#include +#include +#include #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME("osl"); @@ -72,6 +79,72 @@ static unsigned int acpi_irq_irq; static acpi_osd_handler acpi_irq_handler; 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]; + +/* + * "Ode to _OSI(Linux)" + * + * osi_linux -- Control response to BIOS _OSI(Linux) query. + * + * As Linux evolves, the features that it supports change. + * So an OSI string such as "Linux" is not specific enough + * to be useful across multiple versions of Linux. It + * doesn't identify any particular feature, interface, + * or even any particular version of Linux... + * + * Unfortunately, Linux-2.6.22 and earlier responded "yes" + * to a BIOS _OSI(Linux) query. When + * a reference mobile BIOS started using it, its use + * started to spread to many vendor platforms. + * As it is not supportable, we need to halt that spread. + * + * Today, most BIOS references to _OSI(Linux) are noise -- + * they have no functional effect and are just dead code + * carried over from the reference BIOS. + * + * The next most common case is that _OSI(Linux) harms Linux, + * usually by causing the BIOS to follow paths that are + * not tested during Windows validation. + * + * Finally, there is a short list of platforms + * where OSI(Linux) benefits Linux. + * + * In Linux-2.6.23, OSI(Linux) is first disabled by default. + * DMI is used to disable the dmesg warning about OSI(Linux) + * on platforms where it is known to have no effect. + * But a dmesg warning remains for systems where + * we do not know if OSI(Linux) is good or bad for the system. + * DMI is also used to enable OSI(Linux) for the machines + * that are known to need it. + * + * BIOS writers should NOT query _OSI(Linux) on future systems. + * It will be ignored by default, and to get Linux to + * not ignore it will require a kernel source update to + * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation. + */ +#define OSI_LINUX_ENABLE 0 + +static struct osi_linux { + unsigned int enable:1; + unsigned int dmi:1; + unsigned int cmdline:1; + unsigned int known:1; +} osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0}; static void __init acpi_request_region (struct acpi_generic_address *addr, unsigned int length, char *desc) @@ -121,25 +194,17 @@ static int __init acpi_reserve_resources(void) } device_initcall(acpi_reserve_resources); -acpi_status acpi_os_initialize(void) +acpi_status __init acpi_os_initialize(void) { return AE_OK; } acpi_status acpi_os_initialize1(void) { - /* - * Initialize PCI configuration space access, as we'll need to access - * it while walking the namespace (bus 0 and root bridges w/ _BBNs). - */ - if (!raw_pci_ops) { - printk(KERN_ERR PREFIX - "Access to PCI configuration space unavailable\n"); - return AE_NULL_ENTRY; - } kacpid_wq = create_singlethread_workqueue("kacpid"); + kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify"); BUG_ON(!kacpid_wq); - + BUG_ON(!kacpi_notify_wq); return AE_OK; } @@ -151,6 +216,7 @@ acpi_status acpi_os_terminate(void) } destroy_workqueue(kacpid_wq); + destroy_workqueue(kacpi_notify_wq); return AE_OK; } @@ -163,8 +229,6 @@ void acpi_os_printf(const char *fmt, ...) va_end(args); } -EXPORT_SYMBOL(acpi_os_printf); - void acpi_os_vprintf(const char *fmt, va_list args) { static char buffer[512]; @@ -194,11 +258,16 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) "System description tables not found\n"); return 0; } - } else - return acpi_find_rsdp(); + } else { + acpi_physical_address pa = 0; + + acpi_find_root_pointer(&pa); + return pa; + } } -void __iomem *acpi_os_map_memory(acpi_physical_address phys, acpi_size size) +void __iomem *__init_refok +acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { if (phys > ULONG_MAX) { printk(KERN_ERR PREFIX "Cannot map memory that high\n"); @@ -263,20 +332,33 @@ acpi_os_table_override(struct acpi_table_header * existing_table, if (!existing_table || !new_table) return AE_BAD_PARAMETER; + *new_table = NULL; + #ifdef CONFIG_ACPI_CUSTOM_DSDT if (strncmp(existing_table->signature, "DSDT", 4) == 0) *new_table = (struct acpi_table_header *)AmlCode; - else - *new_table = NULL; -#else - *new_table = NULL; #endif + if (*new_table != NULL) { + printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], " + "this is unsafe: tainting kernel\n", + existing_table->signature, + existing_table->oem_table_id); + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); + } return AE_OK; } static irqreturn_t acpi_irq(int irq, void *dev_id) { - return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE; + u32 handled; + + handled = (*acpi_irq_handler) (acpi_irq_context); + + if (handled) { + acpi_irq_handled++; + return IRQ_HANDLED; + } else + return IRQ_NONE; } acpi_status @@ -285,6 +367,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, { unsigned int irq; + acpi_irq_stats_init(); + /* * Ignore the GSI from the core, and use the value in our copy of the * FADT. It may not be the same if an interrupt source override exists @@ -328,8 +412,6 @@ void acpi_os_sleep(acpi_integer ms) schedule_timeout_interruptible(msecs_to_jiffies(ms)); } -EXPORT_SYMBOL(acpi_os_sleep); - void acpi_os_stall(u32 us) { while (us) { @@ -343,8 +425,6 @@ void acpi_os_stall(u32 us) } } -EXPORT_SYMBOL(acpi_os_stall); - /* * Support ACPI 3.0 AML Timer operand * Returns 64-bit free-running, monotonically increasing timer @@ -374,17 +454,14 @@ acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width) if (!value) value = &dummy; - switch (width) { - case 8: + *value = 0; + if (width <= 8) { *(u8 *) value = inb(port); - break; - case 16: + } else if (width <= 16) { *(u16 *) value = inw(port); - break; - case 32: + } else if (width <= 32) { *(u32 *) value = inl(port); - break; - default: + } else { BUG(); } @@ -395,17 +472,13 @@ EXPORT_SYMBOL(acpi_os_read_port); acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width) { - switch (width) { - case 8: + if (width <= 8) { outb(value, port); - break; - case 16: + } else if (width <= 16) { outw(value, port); - break; - case 32: + } else if (width <= 32) { outl(value, port); - break; - default: + } else { BUG(); } @@ -471,7 +544,7 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) acpi_status acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, - void *value, u32 width) + u32 *value, u32 width) { int result, size; @@ -492,17 +565,13 @@ acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, return AE_ERROR; } - BUG_ON(!raw_pci_ops); - - result = raw_pci_ops->read(pci_id->segment, pci_id->bus, - PCI_DEVFN(pci_id->device, pci_id->function), - reg, size, value); + result = raw_pci_read(pci_id->segment, pci_id->bus, + PCI_DEVFN(pci_id->device, pci_id->function), + reg, size, value); return (result ? AE_ERROR : AE_OK); } -EXPORT_SYMBOL(acpi_os_read_pci_configuration); - acpi_status acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, acpi_integer value, u32 width) @@ -523,11 +592,9 @@ acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, return AE_ERROR; } - BUG_ON(!raw_pci_ops); - - result = raw_pci_ops->write(pci_id->segment, pci_id->bus, - PCI_DEVFN(pci_id->device, pci_id->function), - reg, size, value); + result = raw_pci_write(pci_id->segment, pci_id->bus, + PCI_DEVFN(pci_id->device, pci_id->function), + reg, size, value); return (result ? AE_ERROR : AE_OK); } @@ -543,7 +610,6 @@ static void acpi_os_derive_pci_id_2(acpi_handle rhandle, /* upper bound */ acpi_status status; unsigned long temp; acpi_object_type type; - u8 tu8; acpi_get_parent(chandle, &handle); if (handle != rhandle) { @@ -558,6 +624,7 @@ static void acpi_os_derive_pci_id_2(acpi_handle rhandle, /* upper bound */ acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &temp); if (ACPI_SUCCESS(status)) { + u32 val; pci_id->device = ACPI_HIWORD(ACPI_LODWORD(temp)); pci_id->function = ACPI_LOWORD(ACPI_LODWORD(temp)); @@ -566,24 +633,24 @@ static void acpi_os_derive_pci_id_2(acpi_handle rhandle, /* upper bound */ /* any nicer way to get bus number of bridge ? */ status = - acpi_os_read_pci_configuration(pci_id, 0x0e, &tu8, + acpi_os_read_pci_configuration(pci_id, 0x0e, &val, 8); if (ACPI_SUCCESS(status) - && ((tu8 & 0x7f) == 1 || (tu8 & 0x7f) == 2)) { + && ((val & 0x7f) == 1 || (val & 0x7f) == 2)) { status = acpi_os_read_pci_configuration(pci_id, 0x18, - &tu8, 8); + &val, 8); if (!ACPI_SUCCESS(status)) { /* Certainly broken... FIX ME */ return; } *is_bridge = 1; - pci_id->bus = tu8; + pci_id->bus = val; status = acpi_os_read_pci_configuration(pci_id, 0x19, - &tu8, 8); + &val, 8); if (ACPI_SUCCESS(status)) { - *bus_number = tu8; + *bus_number = val; } } else *is_bridge = 0; @@ -604,14 +671,12 @@ void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */ static void acpi_os_execute_deferred(struct work_struct *work) { struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); - if (!dpc) { printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); return; } dpc->function(dpc->context); - kfree(dpc); return; @@ -637,15 +702,13 @@ acpi_status acpi_os_execute(acpi_execute_type type, { acpi_status status = AE_OK; struct acpi_os_dpc *dpc; - - ACPI_FUNCTION_TRACE("os_queue_for_execution"); - + struct workqueue_struct *queue; ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); if (!function) - return_ACPI_STATUS(AE_BAD_PARAMETER); + return AE_BAD_PARAMETER; /* * Allocate/initialize DPC structure. Note that this memory will be @@ -664,13 +727,13 @@ acpi_status acpi_os_execute(acpi_execute_type type, dpc->context = context; INIT_WORK(&dpc->work, acpi_os_execute_deferred); - if (!queue_work(kacpid_wq, &dpc->work)) { + queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq; + if (!queue_work(queue, &dpc->work)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Call to queue_work() failed.\n")); - kfree(dpc); + "Call to queue_work() failed.\n")); status = AE_ERROR; + kfree(dpc); } - return_ACPI_STATUS(status); } @@ -706,7 +769,6 @@ acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle) { struct semaphore *sem = NULL; - sem = acpi_os_allocate(sizeof(struct semaphore)); if (!sem) return AE_NO_MEMORY; @@ -722,8 +784,6 @@ acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle) return AE_OK; } -EXPORT_SYMBOL(acpi_os_create_semaphore); - /* * TODO: A better way to delete semaphores? Linux doesn't have a * 'delete_semaphore()' function -- may result in an invalid @@ -735,36 +795,28 @@ acpi_status acpi_os_delete_semaphore(acpi_handle handle) { struct semaphore *sem = (struct semaphore *)handle; - if (!sem) return AE_BAD_PARAMETER; ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Deleting semaphore[%p].\n", handle)); + BUG_ON(!list_empty(&sem->wait_list)); kfree(sem); sem = NULL; return AE_OK; } -EXPORT_SYMBOL(acpi_os_delete_semaphore); - /* - * TODO: The kernel doesn't have a 'down_timeout' function -- had to - * improvise. The process is to sleep for one scheduler quantum - * until the semaphore becomes available. Downside is that this - * may result in starvation for timeout-based waits when there's - * lots of semaphore activity. - * * TODO: Support for units > 1? */ acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout) { acpi_status status = AE_OK; struct semaphore *sem = (struct semaphore *)handle; + long jiffies; int ret = 0; - if (!sem || (units < 1)) return AE_BAD_PARAMETER; @@ -774,58 +826,14 @@ acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout) ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Waiting for semaphore[%p|%d|%d]\n", handle, units, timeout)); - /* - * This can be called during resume with interrupts off. - * Like boot-time, we should be single threaded and will - * always get the lock if we try -- timeout or not. - * If this doesn't succeed, then we will oops courtesy of - * might_sleep() in down(). - */ - if (!down_trylock(sem)) - return AE_OK; - - switch (timeout) { - /* - * No Wait: - * -------- - * A zero timeout value indicates that we shouldn't wait - just - * acquire the semaphore if available otherwise return AE_TIME - * (a.k.a. 'would block'). - */ - case 0: - if (down_trylock(sem)) - status = AE_TIME; - break; - - /* - * Wait Indefinitely: - * ------------------ - */ - case ACPI_WAIT_FOREVER: - down(sem); - break; - - /* - * Wait w/ Timeout: - * ---------------- - */ - default: - // TODO: A better timeout algorithm? - { - int i = 0; - static const int quantum_ms = 1000 / HZ; - - ret = down_trylock(sem); - for (i = timeout; (i > 0 && ret != 0); i -= quantum_ms) { - schedule_timeout_interruptible(1); - ret = down_trylock(sem); - } - - if (ret != 0) - status = AE_TIME; - } - break; - } + if (timeout == ACPI_WAIT_FOREVER) + jiffies = MAX_SCHEDULE_TIMEOUT; + else + jiffies = msecs_to_jiffies(timeout); + + ret = down_timeout(sem, jiffies); + if (ret) + status = AE_TIME; if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, @@ -841,8 +849,6 @@ acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout) return status; } -EXPORT_SYMBOL(acpi_os_wait_semaphore); - /* * TODO: Support for units > 1? */ @@ -850,7 +856,6 @@ acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units) { struct semaphore *sem = (struct semaphore *)handle; - if (!sem || (units < 1)) return AE_BAD_PARAMETER; @@ -865,8 +870,6 @@ acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units) return AE_OK; } -EXPORT_SYMBOL(acpi_os_signal_semaphore); - #ifdef ACPI_FUTURE_USAGE u32 acpi_os_get_line(char *buffer) { @@ -910,8 +913,6 @@ acpi_status acpi_os_signal(u32 function, void *info) return AE_OK; } -EXPORT_SYMBOL(acpi_os_signal); - static int __init acpi_os_name_setup(char *str) { char *p = acpi_os_name; @@ -936,20 +937,62 @@ static int __init acpi_os_name_setup(char *str) __setup("acpi_os_name=", acpi_os_name_setup); +static void __init set_osi_linux(unsigned int enable) +{ + if (osi_linux.enable != enable) { + osi_linux.enable = enable; + printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n", + enable ? "Add": "Delet"); + } + return; +} + +static void __init acpi_cmdline_osi_linux(unsigned int enable) +{ + osi_linux.cmdline = 1; /* cmdline set the default */ + set_osi_linux(enable); + + return; +} + +void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) +{ + osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */ + + printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + + if (enable == -1) + return; + + osi_linux.known = 1; /* DMI knows which OSI(Linux) default needed */ + + set_osi_linux(enable); + + return; +} + /* - * _OSI control + * Modify the list of "OS Interfaces" reported to BIOS via _OSI + * * empty string disables _OSI - * TBD additional string adds to _OSI + * string starting with '!' disables that string + * otherwise string is added to list, augmenting built-in strings */ -static int __init acpi_osi_setup(char *str) +int __init acpi_osi_setup(char *str) { if (str == NULL || *str == '\0') { printk(KERN_INFO PREFIX "_OSI method disabled\n"); acpi_gbl_create_osi_method = FALSE; - } else { - /* TBD */ - printk(KERN_ERR PREFIX "_OSI additional string ignored -- %s\n", - str); + } else if (!strcmp("!Linux", str)) { + acpi_cmdline_osi_linux(0); /* !enable */ + } else if (*str == '!') { + if (acpi_osi_invalidate(++str) == AE_OK) + printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); + } else if (!strcmp("Linux", str)) { + acpi_cmdline_osi_linux(1); /* enable */ + } else if (*osi_additional_string == '\0') { + strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); + printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); } return 1; @@ -989,13 +1032,127 @@ static int __init acpi_wake_gpes_always_on_setup(char *str) __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); -/* - * max_cstate is defined in the base kernel so modules can - * change it w/o depending on the state of the processor module. +/* 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 + * */ -unsigned int max_cstate = ACPI_PROCESSOR_MAX_POWER; +#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; +} -EXPORT_SYMBOL(max_cstate); +__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("%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. @@ -1039,7 +1196,7 @@ void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags) acpi_status acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t ** cache) { - *cache = kmem_cache_create(name, size, 0, 0, NULL, NULL); + *cache = kmem_cache_create(name, size, 0, 0, NULL); if (*cache == NULL) return AE_ERROR; else @@ -1103,6 +1260,34 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) return (AE_OK); } +/** + * acpi_dmi_dump - dump DMI slots needed for blacklist entry + * + * Returns 0 on success + */ +static int acpi_dmi_dump(void) +{ + + if (!dmi_available) + return -1; + + printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n", + dmi_get_system_info(DMI_SYS_VENDOR)); + printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", + dmi_get_system_info(DMI_PRODUCT_NAME)); + printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", + dmi_get_system_info(DMI_PRODUCT_VERSION)); + printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", + dmi_get_system_info(DMI_BOARD_NAME)); + printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", + dmi_get_system_info(DMI_BIOS_VENDOR)); + printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", + dmi_get_system_info(DMI_BIOS_DATE)); + + return 0; +} + + /****************************************************************************** * * FUNCTION: acpi_os_validate_interface @@ -1119,11 +1304,37 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) acpi_status acpi_os_validate_interface (char *interface) { + if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) + return AE_OK; + if (!strcmp("Linux", interface)) { + + printk(KERN_NOTICE PREFIX + "BIOS _OSI(Linux) query %s%s\n", + osi_linux.enable ? "honored" : "ignored", + osi_linux.cmdline ? " via cmdline" : + osi_linux.dmi ? " via DMI" : ""); + + if (!osi_linux.dmi) { + if (acpi_dmi_dump()) + printk(KERN_NOTICE PREFIX + "[please extract dmidecode output]\n"); + printk(KERN_NOTICE PREFIX + "Please send DMI info above to " + "linux-acpi@vger.kernel.org\n"); + } + if (!osi_linux.known && !osi_linux.cmdline) { + printk(KERN_NOTICE PREFIX + "If \"acpi_osi=%sLinux\" works better, " + "please notify linux-acpi@vger.kernel.org\n", + osi_linux.enable ? "!" : ""); + } - return AE_SUPPORT; + if (osi_linux.enable) + return AE_OK; + } + return AE_SUPPORT; } - /****************************************************************************** * * FUNCTION: acpi_os_validate_address @@ -1144,11 +1355,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