diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
| commit | 3f1fe48a36b0b6722dc3fd421d93512bac138e9a (patch) | |
| tree | b767d7f6e26bc64334d3f14f8422d187239dc45d /drivers/acpi/utils.c | |
| download | linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.tar.gz linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.zip | |
Merge tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linuxgrafted
Pull io_uring fixes from Jens Axboe:
- Fix a task_work add use-after-free with SQPOLL.
The sqpoll thread could pop and complete the last request while
io_req_normal_work_add() was still looking at them after the mpscq
push.
Use the same approach as DEFER_TASKRUN to protect from that, holding
an RCU read lock across the add, and have exit wait for an RCU grace
period for SQPOLL rings as well.
- CQE32 ring fixes: correct the free entry check for 32b CQEs, zero the
big_cqe for aux CQEs, and only post the dummy skip CQE on CQE_MIXED
rings
- Mark the source filter table as COW when cloning bpf filters, so
registering another filter on the source doesn't modify the shared
table in place
- Initialize the task context before running the BPF loop
- Requeue zcrx multishot receives stopped by a local resource
- End a TX_TIMESTAMP multishot cmd when the CQ is full (lollipopkit)
* tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux:
io_uring: fix task_work add use-after-free with SQPOLL
io_uring/cmd_net: end TX_TIMESTAMP multishot when the CQ is full
io_uring/zcrx: requeue multishot receives stopped by a local resource
io_uring: initialize task context before running the BPF loop
io_uring: zero big_cqe for aux CQEs on CQE32 rings
io_uring: fix free entry check for 32b CQEs on CQE32 rings
io_uring: only post the dummy skip CQE on CQE_MIXED rings
io_uring/bpf_filter: mark source as COW when cloning filters
Diffstat (limited to 'drivers/acpi/utils.c')
| -rw-r--r-- | drivers/acpi/utils.c | 1101 |
1 files changed, 1101 insertions, 0 deletions
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c new file mode 100644 index 000000000..d499b7257 --- /dev/null +++ b/drivers/acpi/utils.c @@ -0,0 +1,1101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * acpi_utils.c - ACPI Utility Functions ($Revision: 10 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + */ + +#define pr_fmt(fmt) "ACPI: utils: " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/hardirq.h> +#include <linux/acpi.h> +#include <linux/dynamic_debug.h> + +#include "internal.h" +#include "sleep.h" + +/* -------------------------------------------------------------------------- + Object Evaluation Helpers + -------------------------------------------------------------------------- */ +static void acpi_util_eval_error(acpi_handle h, acpi_string p, acpi_status s) +{ + acpi_handle_debug(h, "Evaluate [%s]: %s\n", p, acpi_format_exception(s)); +} + +acpi_status +acpi_extract_package(union acpi_object *package, + struct acpi_buffer *format, struct acpi_buffer *buffer) +{ + u32 size_required = 0; + u32 tail_offset = 0; + char *format_string = NULL; + u32 format_count = 0; + u32 i = 0; + u8 *head = NULL; + u8 *tail = NULL; + + + if (!package || (package->type != ACPI_TYPE_PACKAGE) + || (package->package.count < 1)) { + pr_debug("Invalid package argument\n"); + return AE_BAD_PARAMETER; + } + + if (!format || !format->pointer || (format->length < 1)) { + pr_debug("Invalid format argument\n"); + return AE_BAD_PARAMETER; + } + + if (!buffer) { + pr_debug("Invalid buffer argument\n"); + return AE_BAD_PARAMETER; + } + + format_count = (format->length / sizeof(char)) - 1; + if (format_count > package->package.count) { + pr_debug("Format specifies more objects [%d] than present [%d]\n", + format_count, package->package.count); + return AE_BAD_DATA; + } + + format_string = format->pointer; + + /* + * Calculate size_required. + */ + for (i = 0; i < format_count; i++) { + + union acpi_object *element = &(package->package.elements[i]); + + switch (element->type) { + + case ACPI_TYPE_INTEGER: + switch (format_string[i]) { + case 'N': + size_required += sizeof(u64); + tail_offset += sizeof(u64); + break; + case 'S': + size_required += + sizeof(char *) + sizeof(u64) + + sizeof(char); + tail_offset += sizeof(char *); + break; + default: + pr_debug("Invalid package element [%d]: got number, expected [%c]\n", + i, format_string[i]); + return AE_BAD_DATA; + } + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + switch (format_string[i]) { + case 'S': + size_required += + sizeof(char *) + + (element->string.length * sizeof(char)) + + sizeof(char); + tail_offset += sizeof(char *); + break; + case 'B': + size_required += + sizeof(u8 *) + element->buffer.length; + tail_offset += sizeof(u8 *); + break; + default: + pr_debug("Invalid package element [%d] got string/buffer, expected [%c]\n", + i, format_string[i]); + return AE_BAD_DATA; + } + break; + case ACPI_TYPE_LOCAL_REFERENCE: + switch (format_string[i]) { + case 'R': + size_required += sizeof(void *); + tail_offset += sizeof(void *); + break; + default: + pr_debug("Invalid package element [%d] got reference, expected [%c]\n", + i, format_string[i]); + return AE_BAD_DATA; + } + break; + + case ACPI_TYPE_PACKAGE: + default: + pr_debug("Unsupported element at index=%d\n", i); + /* TBD: handle nested packages... */ + return AE_SUPPORT; + } + } + + /* + * Validate output buffer. + */ + if (buffer->length == ACPI_ALLOCATE_BUFFER) { + buffer->pointer = ACPI_ALLOCATE_ZEROED(size_required); + if (!buffer->pointer) + return AE_NO_MEMORY; + buffer->length = size_required; + } else { + if (buffer->length < size_required) { + buffer->length = size_required; + return AE_BUFFER_OVERFLOW; + } else if (buffer->length != size_required || + !buffer->pointer) { + return AE_BAD_PARAMETER; + } + } + + head = buffer->pointer; + tail = buffer->pointer + tail_offset; + + /* + * Extract package data. + */ + for (i = 0; i < format_count; i++) { + + u8 **pointer = NULL; + union acpi_object *element = &(package->package.elements[i]); + + switch (element->type) { + + case ACPI_TYPE_INTEGER: + switch (format_string[i]) { + case 'N': + *((u64 *) head) = + element->integer.value; + head += sizeof(u64); + break; + case 'S': + pointer = (u8 **) head; + *pointer = tail; + *((u64 *) tail) = + element->integer.value; + head += sizeof(u64 *); + tail += sizeof(u64); + /* NULL terminate string */ + *tail = (char)0; + tail += sizeof(char); + break; + default: + /* Should never get here */ + break; + } + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + switch (format_string[i]) { + case 'S': + pointer = (u8 **) head; + *pointer = tail; + memcpy(tail, element->string.pointer, + element->string.length); + head += sizeof(char *); + tail += element->string.length * sizeof(char); + /* NULL terminate string */ + *tail = (char)0; + tail += sizeof(char); + break; + case 'B': + pointer = (u8 **) head; + *pointer = tail; + memcpy(tail, element->buffer.pointer, + element->buffer.length); + head += sizeof(u8 *); + tail += element->buffer.length; + break; + default: + /* Should never get here */ + break; + } + break; + case ACPI_TYPE_LOCAL_REFERENCE: + switch (format_string[i]) { + case 'R': + *(void **)head = + (void *)element->reference.handle; + head += sizeof(void *); + break; + default: + /* Should never get here */ + break; + } + break; + case ACPI_TYPE_PACKAGE: + /* TBD: handle nested packages... */ + default: + /* Should never get here */ + break; + } + } + + return AE_OK; +} + +EXPORT_SYMBOL(acpi_extract_package); + +acpi_status +acpi_evaluate_integer(acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *arguments, unsigned long long *data) +{ + acpi_status status = AE_OK; + union acpi_object element; + struct acpi_buffer buffer = { 0, NULL }; + + if (!data) + return AE_BAD_PARAMETER; + + buffer.length = sizeof(union acpi_object); + buffer.pointer = &element; + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) { + acpi_util_eval_error(handle, pathname, status); + return status; + } + + if (element.type != ACPI_TYPE_INTEGER) { + acpi_util_eval_error(handle, pathname, AE_BAD_DATA); + return AE_BAD_DATA; + } + + *data = element.integer.value; + + acpi_handle_debug(handle, "Return value [%llu]\n", *data); + + return AE_OK; +} + +EXPORT_SYMBOL(acpi_evaluate_integer); + +int acpi_get_local_u64_address(acpi_handle handle, u64 *addr) +{ + acpi_status status; + + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, addr); + if (ACPI_FAILURE(status)) + return -ENODATA; + return 0; +} +EXPORT_SYMBOL(acpi_get_local_u64_address); + +int acpi_get_local_address(acpi_handle handle, u32 *addr) +{ + u64 adr; + int ret; + + ret = acpi_get_local_u64_address(handle, &adr); + if (ret < 0) + return ret; + *addr = (u32)adr; + return 0; +} +EXPORT_SYMBOL(acpi_get_local_address); + +#define ACPI_MAX_SUB_BUF_SIZE 9 + +const char *acpi_get_subsystem_id(acpi_handle handle) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + const char *sub; + size_t len; + + status = acpi_evaluate_object(handle, METHOD_NAME__SUB, NULL, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "Reading ACPI _SUB failed: %#x\n", status); + return ERR_PTR(-ENODATA); + } + + obj = buffer.pointer; + if (obj->type == ACPI_TYPE_STRING) { + len = strlen(obj->string.pointer); + if (len < ACPI_MAX_SUB_BUF_SIZE && len > 0) { + sub = kstrdup(obj->string.pointer, GFP_KERNEL); + if (!sub) + sub = ERR_PTR(-ENOMEM); + } else { + acpi_handle_err(handle, "ACPI _SUB Length %zu is Invalid\n", len); + sub = ERR_PTR(-ENODATA); + } + } else { + acpi_handle_warn(handle, "Warning ACPI _SUB did not return a string\n"); + sub = ERR_PTR(-ENODATA); + } + + acpi_os_free(buffer.pointer); + + return sub; +} +EXPORT_SYMBOL_GPL(acpi_get_subsystem_id); + +bool acpi_evaluate_reference(acpi_handle handle, acpi_string pathname, + struct acpi_object_list *arguments, + struct acpi_handle_list *list) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package; + acpi_status status; + bool ret = false; + u32 i; + + if (!list) + return false; + + /* Evaluate object. */ + + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) + goto end; + + package = buffer.pointer; + + if (buffer.length == 0 || !package || + package->type != ACPI_TYPE_PACKAGE || !package->package.count) + goto err; + + list->count = package->package.count; + list->handles = kzalloc_objs(*list->handles, list->count); + if (!list->handles) + goto err_clear; + + /* Extract package data. */ + + for (i = 0; i < list->count; i++) { + union acpi_object *element = &(package->package.elements[i]); + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE || + !element->reference.handle) + goto err_free; + + /* Get the acpi_handle. */ + + list->handles[i] = element->reference.handle; + acpi_handle_debug(list->handles[i], "Found in reference list\n"); + } + + ret = true; + +end: + kfree(buffer.pointer); + + return ret; + +err_free: + kfree(list->handles); + list->handles = NULL; + +err_clear: + list->count = 0; + +err: + acpi_util_eval_error(handle, pathname, status); + goto end; +} + +EXPORT_SYMBOL(acpi_evaluate_reference); + +/** + * acpi_handle_list_equal - Check if two ACPI handle lists are the same + * @list1: First list to compare. + * @list2: Second list to compare. + * + * Return true if the given ACPI handle lists are of the same size and + * contain the same ACPI handles in the same order. Otherwise, return false. + */ +bool acpi_handle_list_equal(struct acpi_handle_list *list1, + struct acpi_handle_list *list2) +{ + return list1->count == list2->count && + !memcmp(list1->handles, list2->handles, + list1->count * sizeof(*list1->handles)); +} +EXPORT_SYMBOL_GPL(acpi_handle_list_equal); + +/** + * acpi_handle_list_replace - Replace one ACPI handle list with another + * @dst: ACPI handle list to replace. + * @src: Source ACPI handle list. + * + * Free the handles table in @dst, move the handles table from @src to @dst, + * copy count from @src to @dst and clear @src. + */ +void acpi_handle_list_replace(struct acpi_handle_list *dst, + struct acpi_handle_list *src) +{ + if (dst->count) + kfree(dst->handles); + + dst->count = src->count; + dst->handles = src->handles; + + src->handles = NULL; + src->count = 0; +} +EXPORT_SYMBOL_GPL(acpi_handle_list_replace); + +/** + * acpi_handle_list_free - Free the handles table in an ACPI handle list + * @list: ACPI handle list to free. + * + * Free the handles table in @list and clear its count field. + */ +void acpi_handle_list_free(struct acpi_handle_list *list) +{ + if (!list->count) + return; + + kfree(list->handles); + list->count = 0; +} +EXPORT_SYMBOL_GPL(acpi_handle_list_free); + +/** + * acpi_device_dep - Check ACPI device dependency + * @target: ACPI handle of the target ACPI device. + * @match: ACPI handle to look up in the target's _DEP list. + * + * Return true if @match is present in the list returned by _DEP for + * @target or false otherwise. + */ +bool acpi_device_dep(acpi_handle target, acpi_handle match) +{ + struct acpi_handle_list dep_devices; + bool ret = false; + int i; + + if (!acpi_has_method(target, "_DEP")) + return false; + + if (!acpi_evaluate_reference(target, "_DEP", NULL, &dep_devices)) { + acpi_handle_debug(target, "Failed to evaluate _DEP.\n"); + return false; + } + + for (i = 0; i < dep_devices.count; i++) { + if (dep_devices.handles[i] == match) { + ret = true; + break; + } + } + + acpi_handle_list_free(&dep_devices); + return ret; +} +EXPORT_SYMBOL_GPL(acpi_device_dep); + +bool +acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *output; + + status = acpi_evaluate_object(handle, "_PLD", NULL, &buffer); + if (ACPI_FAILURE(status)) + return false; + + output = buffer.pointer; + + if (!output || output->type != ACPI_TYPE_PACKAGE + || !output->package.count + || output->package.elements[0].type != ACPI_TYPE_BUFFER + || output->package.elements[0].buffer.length < ACPI_PLD_REV1_BUFFER_SIZE) { + status = AE_TYPE; + goto out; + } + + status = acpi_decode_pld_buffer( + output->package.elements[0].buffer.pointer, + output->package.elements[0].buffer.length, + pld); + +out: + kfree(buffer.pointer); + return ACPI_SUCCESS(status); +} +EXPORT_SYMBOL(acpi_get_physical_device_location); + +/** + * acpi_evaluate_ost: Evaluate _OST for hotplug operations + * @handle: ACPI device handle + * @source_event: source event code + * @status_code: status code + * @status_buf: optional detailed information (NULL if none) + * + * Evaluate _OST for hotplug operations. All ACPI hotplug handlers + * must call this function when evaluating _OST for hotplug operations. + * When the platform does not support _OST, this function has no effect. + */ +acpi_status +acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code, + struct acpi_buffer *status_buf) +{ + union acpi_object params[3] = { + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_BUFFER,} + }; + struct acpi_object_list arg_list = {3, params}; + + params[0].integer.value = source_event; + params[1].integer.value = status_code; + if (status_buf != NULL) { + params[2].buffer.pointer = status_buf->pointer; + params[2].buffer.length = status_buf->length; + } else { + params[2].buffer.pointer = NULL; + params[2].buffer.length = 0; + } + + return acpi_evaluate_object(handle, "_OST", &arg_list, NULL); +} +EXPORT_SYMBOL(acpi_evaluate_ost); + +/** + * acpi_handle_path: Return the object path of handle + * @handle: ACPI device handle + * + * Caller must free the returned buffer + */ +char *acpi_handle_path(acpi_handle handle) +{ + struct acpi_buffer buffer = { + .length = ACPI_ALLOCATE_BUFFER, + .pointer = NULL + }; + + if (in_interrupt() || + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK) + return NULL; + return buffer.pointer; +} + +/** + * acpi_handle_printk: Print message with ACPI prefix and object path + * @level: log level + * @handle: ACPI device handle + * @fmt: format string + * + * This function is called through acpi_handle_<level> macros and prints + * a message with ACPI prefix and object path. This function acquires + * the global namespace mutex to obtain an object path. In interrupt + * context, it shows the object path as <n/a>. + */ +void +acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + const char *path; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + path = acpi_handle_path(handle); + printk("%sACPI: %s: %pV", level, path ? path : "<n/a>", &vaf); + + va_end(args); + kfree(path); +} +EXPORT_SYMBOL(acpi_handle_printk); + +#if defined(CONFIG_DYNAMIC_DEBUG) +/** + * __acpi_handle_debug: pr_debug with ACPI prefix and object path + * @descriptor: Dynamic Debug descriptor + * @handle: ACPI device handle + * @fmt: format string + * + * This function is called through acpi_handle_debug macro and debug + * prints a message with ACPI prefix and object path. This function + * acquires the global namespace mutex to obtain an object path. In + * interrupt context, it shows the object path as <n/a>. + */ +void +__acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + const char *path; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + path = acpi_handle_path(handle); + __dynamic_pr_debug(descriptor, "ACPI: %s: %pV", path ? path : "<n/a>", &vaf); + + va_end(args); + kfree(path); +} +EXPORT_SYMBOL(__acpi_handle_debug); +#endif + +/** + * acpi_evaluation_failure_warn - Log evaluation failure warning. + * @handle: Parent object handle. + * @name: Name of the object whose evaluation has failed. + * @status: Status value returned by the failing object evaluation. + */ +void acpi_evaluation_failure_warn(acpi_handle handle, const char *name, + acpi_status status) +{ + acpi_handle_warn(handle, "%s evaluation failed: %s\n", name, + acpi_format_exception(status)); +} +EXPORT_SYMBOL_GPL(acpi_evaluation_failure_warn); + +/** + * acpi_has_method: Check whether @handle has a method named @name + * @handle: ACPI device handle + * @name: name of object or method + * + * Check whether @handle has a method named @name. + */ +bool acpi_has_method(acpi_handle handle, char *name) +{ + acpi_handle tmp; + + return ACPI_SUCCESS(acpi_get_handle(handle, name, &tmp)); +} +EXPORT_SYMBOL(acpi_has_method); + +acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, + u64 arg) +{ + union acpi_object obj = { .type = ACPI_TYPE_INTEGER }; + struct acpi_object_list arg_list = { .count = 1, .pointer = &obj, }; + + obj.integer.value = arg; + + return acpi_evaluate_object(handle, method, &arg_list, NULL); +} +EXPORT_SYMBOL(acpi_execute_simple_method); + +/** + * acpi_evaluate_ej0: Evaluate _EJ0 method for hotplug operations + * @handle: ACPI device handle + * + * Evaluate device's _EJ0 method for hotplug operations. + */ +acpi_status acpi_evaluate_ej0(acpi_handle handle) +{ + acpi_status status; + + status = acpi_execute_simple_method(handle, "_EJ0", 1); + if (status == AE_NOT_FOUND) + acpi_handle_warn(handle, "No _EJ0 support for device\n"); + else if (ACPI_FAILURE(status)) + acpi_handle_warn(handle, "Eject failed (0x%x)\n", status); + + return status; +} + +/** + * acpi_evaluate_lck: Evaluate _LCK method to lock/unlock device + * @handle: ACPI device handle + * @lock: lock device if non-zero, otherwise unlock device + * + * Evaluate device's _LCK method if present to lock/unlock device + */ +acpi_status acpi_evaluate_lck(acpi_handle handle, int lock) +{ + acpi_status status; + + status = acpi_execute_simple_method(handle, "_LCK", !!lock); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + if (lock) + acpi_handle_warn(handle, + "Locking device failed (0x%x)\n", status); + else + acpi_handle_warn(handle, + "Unlocking device failed (0x%x)\n", status); + } + + return status; +} + +/** + * acpi_evaluate_reg: Evaluate _REG method to register OpRegion presence + * @handle: ACPI device handle + * @space_id: ACPI address space id to register OpRegion presence for + * @function: Parameter to pass to _REG one of ACPI_REG_CONNECT or + * ACPI_REG_DISCONNECT + * + * Evaluate device's _REG method to register OpRegion presence. + */ +acpi_status acpi_evaluate_reg(acpi_handle handle, u8 space_id, u32 function) +{ + struct acpi_object_list arg_list; + union acpi_object params[2]; + + params[0].type = ACPI_TYPE_INTEGER; + params[0].integer.value = space_id; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = function; + arg_list.count = 2; + arg_list.pointer = params; + + return acpi_evaluate_object(handle, "_REG", &arg_list, NULL); +} +EXPORT_SYMBOL(acpi_evaluate_reg); + +/** + * acpi_evaluate_dsm - evaluate device's _DSM method + * @handle: ACPI device handle + * @guid: GUID of requested functions, should be 16 bytes + * @rev: revision number of requested function + * @func: requested function number + * @argv4: the function specific parameter + * + * Evaluate device's _DSM method with specified GUID, revision id and + * function number. Caller needs to free the returned object. + * + * Though ACPI defines the fourth parameter for _DSM should be a package, + * some old BIOSes do expect a buffer or an integer etc. + */ +union acpi_object * +acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 func, + union acpi_object *argv4) +{ + acpi_status ret; + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object params[4]; + struct acpi_object_list input = { + .count = 4, + .pointer = params, + }; + + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = 16; + params[0].buffer.pointer = (u8 *)guid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = rev; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = func; + if (argv4) { + params[3] = *argv4; + } else { + params[3].type = ACPI_TYPE_PACKAGE; + params[3].package.count = 0; + params[3].package.elements = NULL; + } + + ret = acpi_evaluate_object(handle, "_DSM", &input, &buf); + if (ACPI_SUCCESS(ret)) + return (union acpi_object *)buf.pointer; + + if (ret != AE_NOT_FOUND) + acpi_handle_warn(handle, + "failed to evaluate _DSM %pUb rev:%lld func:%lld (0x%x)\n", + guid, rev, func, ret); + + return NULL; +} +EXPORT_SYMBOL(acpi_evaluate_dsm); + +/** + * acpi_check_dsm - check if _DSM method supports requested functions. + * @handle: ACPI device handle + * @guid: GUID of requested functions, should be 16 bytes at least + * @rev: revision number of requested functions + * @funcs: bitmap of requested functions + * + * Evaluate device's _DSM method to check whether it supports requested + * functions. Currently only support 64 functions at maximum, should be + * enough for now. + */ +bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs) +{ + int i; + u64 mask = 0; + union acpi_object *obj; + + if (funcs == 0) + return false; + + obj = acpi_evaluate_dsm(handle, guid, rev, 0, NULL); + if (!obj) + return false; + + /* For compatibility, old BIOSes may return an integer */ + if (obj->type == ACPI_TYPE_INTEGER) + mask = obj->integer.value; + else if (obj->type == ACPI_TYPE_BUFFER) + for (i = 0; i < obj->buffer.length && i < 8; i++) + mask |= (((u64)obj->buffer.pointer[i]) << (i * 8)); + ACPI_FREE(obj); + + /* + * Bit 0 indicates whether there's support for any functions other than + * function 0 for the specified GUID and revision. + */ + if ((mask & 0x1) && (mask & funcs) == funcs) + return true; + + return false; +} +EXPORT_SYMBOL(acpi_check_dsm); + +/** + * acpi_dev_uid_to_integer - treat ACPI device _UID as integer + * @adev: ACPI device to get _UID from + * @integer: output buffer for integer + * + * Considers _UID as integer and converts it to @integer. + * + * Returns 0 on success, or negative error code otherwise. + */ +int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer) +{ + const char *uid; + + if (!adev) + return -ENODEV; + + uid = acpi_device_uid(adev); + if (!uid) + return -ENODATA; + + return kstrtou64(uid, 0, integer); +} +EXPORT_SYMBOL(acpi_dev_uid_to_integer); + +/** + * acpi_dev_found - Detect presence of a given ACPI device in the namespace. + * @hid: Hardware ID of the device. + * + * Return %true if the device was present at the moment of invocation. + * Note that if the device is pluggable, it may since have disappeared. + * + * For this function to work, acpi_bus_scan() must have been executed + * which happens in the subsys_initcall() subsection. Hence, do not + * call from a subsys_initcall() or earlier (use acpi_get_devices() + * instead). Calling from module_init() is fine (which is synonymous + * with device_initcall()). + */ +bool acpi_dev_found(const char *hid) +{ + struct acpi_device_bus_id *acpi_device_bus_id; + bool found = false; + + mutex_lock(&acpi_device_lock); + list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) + if (!strcmp(acpi_device_bus_id->bus_id, hid)) { + found = true; + break; + } + mutex_unlock(&acpi_device_lock); + + return found; +} +EXPORT_SYMBOL(acpi_dev_found); + +struct acpi_dev_match_info { + struct acpi_device_id hid[2]; + const char *uid; + s64 hrv; +}; + +static int acpi_dev_match_cb(struct device *dev, const void *data) +{ + struct acpi_device *adev = to_acpi_device(dev); + const struct acpi_dev_match_info *match = data; + unsigned long long hrv; + acpi_status status; + + if (acpi_match_device_ids(adev, match->hid)) + return 0; + + if (match->uid && !acpi_dev_uid_match(adev, match->uid)) + return 0; + + if (match->hrv == -1) + return 1; + + status = acpi_evaluate_integer(adev->handle, "_HRV", NULL, &hrv); + if (ACPI_FAILURE(status)) + return 0; + + return hrv == match->hrv; +} + +/** + * acpi_dev_present - Detect that a given ACPI device is present + * @hid: Hardware ID of the device. + * @uid: Unique ID of the device, pass NULL to not check _UID + * @hrv: Hardware Revision of the device, pass -1 to not check _HRV + * + * Return %true if a matching device was present at the moment of invocation. + * Note that if the device is pluggable, it may since have disappeared. + * + * Note that unlike acpi_dev_found() this function checks the status + * of the device. So for devices which are present in the DSDT, but + * which are disabled (their _STA callback returns 0) this function + * will return false. + * + * For this function to work, acpi_bus_scan() must have been executed + * which happens in the subsys_initcall() subsection. Hence, do not + * call from a subsys_initcall() or earlier (use acpi_get_devices() + * instead). Calling from module_init() is fine (which is synonymous + * with device_initcall()). + */ +bool acpi_dev_present(const char *hid, const char *uid, s64 hrv) +{ + struct acpi_dev_match_info match = {}; + struct device *dev; + + strscpy(match.hid[0].id, hid, sizeof(match.hid[0].id)); + match.uid = uid; + match.hrv = hrv; + + dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb); + put_device(dev); + return !!dev; +} +EXPORT_SYMBOL(acpi_dev_present); + +/** + * acpi_dev_get_next_match_dev - Return the next match of ACPI device + * @adev: Pointer to the previous ACPI device matching this @hid, @uid and @hrv + * @hid: Hardware ID of the device. + * @uid: Unique ID of the device, pass NULL to not check _UID + * @hrv: Hardware Revision of the device, pass -1 to not check _HRV + * + * Return the next match of ACPI device if another matching device was present + * at the moment of invocation, or NULL otherwise. + * + * The caller is responsible for invoking acpi_dev_put() on the returned device. + * On the other hand the function invokes acpi_dev_put() on the given @adev + * assuming that its reference counter had been increased beforehand. + * + * See additional information in acpi_dev_present() as well. + */ +struct acpi_device * +acpi_dev_get_next_match_dev(struct acpi_device *adev, const char *hid, const char *uid, s64 hrv) +{ + struct device *start = adev ? &adev->dev : NULL; + struct acpi_dev_match_info match = {}; + struct device *dev; + + strscpy(match.hid[0].id, hid, sizeof(match.hid[0].id)); + match.uid = uid; + match.hrv = hrv; + + dev = bus_find_device(&acpi_bus_type, start, &match, acpi_dev_match_cb); + acpi_dev_put(adev); + return dev ? to_acpi_device(dev) : NULL; +} +EXPORT_SYMBOL(acpi_dev_get_next_match_dev); + +/** + * acpi_dev_get_first_match_dev - Return the first match of ACPI device + * @hid: Hardware ID of the device. + * @uid: Unique ID of the device, pass NULL to not check _UID + * @hrv: Hardware Revision of the device, pass -1 to not check _HRV + * + * Return the first match of ACPI device if a matching device was present + * at the moment of invocation, or NULL otherwise. + * + * The caller is responsible for invoking acpi_dev_put() on the returned device. + * + * See additional information in acpi_dev_present() as well. + */ +struct acpi_device * +acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv) +{ + return acpi_dev_get_next_match_dev(NULL, hid, uid, hrv); +} +EXPORT_SYMBOL(acpi_dev_get_first_match_dev); + +/** + * acpi_reduced_hardware - Return if this is an ACPI-reduced-hw machine + * + * Return true when running on an ACPI-reduced-hw machine, false otherwise. + */ +bool acpi_reduced_hardware(void) +{ + return acpi_gbl_reduced_hardware; +} +EXPORT_SYMBOL_GPL(acpi_reduced_hardware); + +/* + * acpi_backlight= handling, this is done here rather then in video_detect.c + * because __setup cannot be used in modules. + */ +char acpi_video_backlight_string[16]; +EXPORT_SYMBOL(acpi_video_backlight_string); + +static int __init acpi_backlight(char *str) +{ + strscpy(acpi_video_backlight_string, str, + sizeof(acpi_video_backlight_string)); + return 1; +} +__setup("acpi_backlight=", acpi_backlight); + +/** + * acpi_dev_is_video_device - test if device matches against ACPI video device IDs + * @adev: ACPI device to test + * + * Return: true when matches, otherwise false. + */ +bool acpi_dev_is_video_device(struct acpi_device *adev) +{ + static const struct acpi_device_id video_device_ids[] = { + { .id = ACPI_VIDEO_HID }, + { } + }; + + return adev && !acpi_match_device_ids(adev, video_device_ids); +} +EXPORT_SYMBOL(acpi_dev_is_video_device); + +/** + * acpi_match_platform_list - Check if the system matches with a given list + * @plat: pointer to acpi_platform_list table terminated by a NULL entry + * + * Return the matched index if the system is found in the platform list. + * Otherwise, return a negative error code. + */ +int acpi_match_platform_list(const struct acpi_platform_list *plat) +{ + struct acpi_table_header hdr; + int idx = 0; + + if (acpi_disabled) + return -ENODEV; + + for (; plat->oem_id[0]; plat++, idx++) { + if (ACPI_FAILURE(acpi_get_table_header(plat->table, 0, &hdr))) + continue; + + if (strncmp(plat->oem_id, hdr.oem_id, ACPI_OEM_ID_SIZE)) + continue; + + if (strncmp(plat->oem_table_id, hdr.oem_table_id, ACPI_OEM_TABLE_ID_SIZE)) + continue; + + if ((plat->pred == all_versions) || + (plat->pred == less_than_or_equal && hdr.oem_revision <= plat->oem_revision) || + (plat->pred == greater_than_or_equal && hdr.oem_revision >= plat->oem_revision) || + (plat->pred == equal && hdr.oem_revision == plat->oem_revision)) + return idx; + } + + return -ENODEV; +} +EXPORT_SYMBOL(acpi_match_platform_list); |
