// SPDX-License-Identifier: GPL-2.0-or-later/* * acpi_bus.c - ACPI Bus Driver ($Revision: 80 $) * * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> */ #define pr_fmt(fmt) "ACPI: " fmt #include <linux/module.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/sched.h>#include <linux/pm.h>#include <linux/device.h>#include <linux/proc_fs.h>#include <linux/acpi.h>#include <linux/slab.h>#include <linux/regulator/machine.h>#include <linux/workqueue.h>#include <linux/reboot.h>#include <linux/delay.h>#ifdef CONFIG_X86#include <asm/mpspec.h>#include <linux/dmi.h>#endif#include <linux/acpi_viot.h>#include <linux/pci.h>#include <acpi/apei.h>#include <linux/suspend.h>#include <linux/prmt.h> #include "internal.h" struct acpi_device *acpi_root;struct proc_dir_entry *acpi_root_dir;EXPORT_SYMBOL(acpi_root_dir); #ifdef CONFIG_X86#ifdef CONFIG_ACPI_CUSTOM_DSDTstatic inline int set_copy_dsdt(const struct dmi_system_id *id){ return 0;}#elsestatic int set_copy_dsdt(const struct dmi_system_id *id){ pr_notice("%s detected - force copy of DSDT to local memory\n", id->ident); acpi_gbl_copy_dsdt_locally = 1; return 0;}#endif static const struct dmi_system_id dsdt_dmi_table[] __initconst = { /* * Invoke DSDT corruption work-around on all Toshiba Satellite. * https://bugzilla.kernel.org/show_bug.cgi?id=14679 */ { .callback = set_copy_dsdt, .ident = "TOSHIBA Satellite", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), }, }, {}};#endif /* -------------------------------------------------------------------------- Device Management -------------------------------------------------------------------------- */ acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta){ acpi_status status; status = acpi_evaluate_integer(handle, "_STA", NULL, sta); if (ACPI_SUCCESS(status)) return AE_OK; if (status == AE_NOT_FOUND) { *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; return AE_OK; } return status;}EXPORT_SYMBOL_GPL(acpi_bus_get_status_handle); int acpi_bus_get_status(struct acpi_device *device){ acpi_status status; unsigned long long sta; if (acpi_device_override_status(device, &sta)) { acpi_set_device_status(device, sta); return 0; } /* Battery devices must have their deps met before calling _STA */ if (acpi_device_is_battery(device) && device->dep_unmet) { acpi_set_device_status(device, 0); return 0; } status = acpi_bus_get_status_handle(device->handle, &sta); if (ACPI_FAILURE(status)) return -ENODEV; if (!device->status.present && device->status.enabled) { pr_info(FW_BUG "Device [%s] status [%08x]: not present and enabled\n", device->pnp.bus_id, (u32)sta); device->status.enabled = 0; /* * The status is clearly invalid, so clear the functional bit as * well to avoid attempting to use the device. */ device->status.functional = 0; } acpi_set_device_status(device, sta); if (device->status.functional && !device->status.present) { pr_debug("Device [%s] status [%08x]: functional but not present\n", device->pnp.bus_id, (u32)sta); } pr_debug("Device [%s] status [%08x]\n", device->pnp.bus_id, (u32)sta); return 0;}EXPORT_SYMBOL(acpi_bus_get_status); void acpi_bus_private_data_handler(acpi_handle handle, void *context){ return;}EXPORT_SYMBOL(acpi_bus_private_data_handler); int acpi_bus_attach_private_data(acpi_handle handle, void *data){ acpi_status status; status = acpi_attach_data(handle, acpi_bus_private_data_handler, data); if (ACPI_FAILURE(status)) { acpi_handle_debug(handle, "Error attaching device data\n"); return -ENODEV; } return 0;}EXPORT_SYMBOL_GPL(acpi_bus_attach_private_data); int acpi_bus_get_private_data(acpi_handle handle, void **data){ acpi_status status; if (!data) return -EINVAL; status = acpi_get_data(handle, acpi_bus_private_data_handler, data); if (ACPI_FAILURE(status)) { acpi_handle_debug(handle, "No context for object\n"); return -ENODEV; } return 0;}EXPORT_SYMBOL_GPL(acpi_bus_get_private_data); void acpi_bus_detach_private_data(acpi_handle handle){ acpi_detach_data(handle, acpi_bus_private_data_handler);}EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); static void acpi_dump_osc_data(acpi_handle handle, const guid_t *guid, int rev, struct acpi_buffer *cap){ u32 *capbuf = cap->pointer; int i; acpi_handle_debug(handle, "_OSC: UUID: %pUL, rev: %d\n", guid, rev); for (i = 0; i < cap->length / sizeof(u32); i++) acpi_handle_debug(handle, "_OSC: capabilities DWORD %i: [%08x]\n", i, capbuf[i]);} #define OSC_ERROR_MASK (OSC_REQUEST_ERROR | OSC_INVALID_UUID_ERROR | \ OSC_INVALID_REVISION_ERROR | \ OSC_CAPABILITIES_MASK_ERROR) static int acpi_eval_osc(acpi_handle handle, guid_t *guid, int rev, struct acpi_buffer *cap, union acpi_object in_params[at_least 4], struct acpi_buffer *output){ struct acpi_object_list input; union acpi_object *out_obj; acpi_status status; in_params[0].type = ACPI_TYPE_BUFFER; in_params[0].buffer.length = sizeof(*guid); in_params[0].buffer.pointer = (u8 *)guid; in_params[1].type = ACPI_TYPE_INTEGER; in_params[1].integer.value = rev; in_params[2].type = ACPI_TYPE_INTEGER; in_params[2].integer.value = cap->length / sizeof(u32); in_params[3].type = ACPI_TYPE_BUFFER; in_params[3].buffer.length = cap->length; in_params[3].buffer.pointer = cap->pointer; input.pointer = in_params; input.count = 4; output->length = ACPI_ALLOCATE_BUFFER; output->pointer = NULL; status = acpi_evaluate_object(handle, "_OSC", &input, output); if (ACPI_FAILURE(status) || !output->length) return -ENODATA; out_obj = output->pointer; if (out_obj->type != ACPI_TYPE_BUFFER || out_obj->buffer.length != cap->length) { acpi_handle_debug(handle, "Invalid _OSC return buffer\n"); acpi_dump_osc_data(handle, guid, rev, cap); ACPI_FREE(out_obj); return -ENODATA; } return 0;} static bool acpi_osc_error_check(acpi_handle handle, guid_t *guid, int rev, struct acpi_buffer *cap, u32 *retbuf){ /* Only take defined error bits into account. */ u32 errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK; u32 *capbuf = cap->pointer; bool fail; /* * If OSC_QUERY_ENABLE is set, ignore the "capabilities masked" * bit because it merely means that some features have not been * acknowledged which is not unexpected. */ if (capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE) errors &= ~OSC_CAPABILITIES_MASK_ERROR; if (!errors) return false; acpi_dump_osc_data(handle, guid, rev, cap); /* * As a rule, fail only if OSC_QUERY_ENABLE is set because otherwise the * acknowledged features need to be controlled. */ fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); if (errors & OSC_REQUEST_ERROR) acpi_handle_debug(handle, "_OSC: request failed\n"); if (errors & OSC_INVALID_UUID_ERROR) { acpi_handle_debug(handle, "_OSC: invalid UUID\n"); /* * Always fail if this bit is set because it means that the * request could not be processed. */ fail = true; } if (errors & OSC_INVALID_REVISION_ERROR) acpi_handle_debug(handle, "_OSC: invalid revision\n"); if (errors & OSC_CAPABILITIES_MASK_ERROR) acpi_handle_debug(handle, "_OSC: capability bits masked\n"); return fail;} acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context){ union acpi_object in_params[4], *out_obj; struct acpi_buffer output; acpi_status status = AE_OK; guid_t guid; u32 *retbuf; int ret; if (!context || !context->cap.pointer || context->cap.length < 2 * sizeof(u32) || guid_parse(context->uuid_str, &guid)) return AE_BAD_PARAMETER; ret = acpi_eval_osc(handle, &guid, context->rev, &context->cap, in_params, &output); if (ret) return AE_ERROR; out_obj = output.pointer; retbuf = (u32 *)out_obj->buffer.pointer; if (acpi_osc_error_check(handle, &guid, context->rev, &context->cap, retbuf)) { status = AE_ERROR; goto out; } context->ret.length = out_obj->buffer.length; context->ret.pointer = kmemdup(retbuf, context->ret.length, GFP_KERNEL); if (!context->ret.pointer) { status = AE_NO_MEMORY; goto out; } status = AE_OK; out: ACPI_FREE(out_obj); return status;}EXPORT_SYMBOL(acpi_run_osc); static int acpi_osc_handshake(acpi_handle handle, const char *uuid_str, int rev, u32 *capbuf, size_t bufsize){ union acpi_object in_params[4], *out_obj; struct acpi_object_list input; struct acpi_buffer cap = { .pointer = capbuf, .length = bufsize * sizeof(u32), }; struct acpi_buffer output; u32 *retbuf, test, errors; guid_t guid; int ret, i; if (!capbuf || bufsize < 2 || guid_parse(uuid_str, &guid)) return -EINVAL; /* First evaluate _OSC with OSC_QUERY_ENABLE set. */ capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; ret = acpi_eval_osc(handle, &guid, rev, &cap, in_params, &output); if (ret) return ret; out_obj = output.pointer; retbuf = (u32 *)out_obj->buffer.pointer; if (acpi_osc_error_check(handle, &guid, rev, &cap, retbuf)) { ret = -ENODATA; goto out; } /* * Clear the feature bits in the capabilities buffer that have not been * acknowledged and clear the return buffer. */ for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) { capbuf[i] &= retbuf[i]; test |= capbuf[i]; retbuf[i] = 0; } /* * If none of the feature bits have been acknowledged, there's nothing * more to do. capbuf[] contains a feature mask of all zeros. */ if (!test) goto out; retbuf[OSC_QUERY_DWORD] = 0; /* * Now evaluate _OSC again (directly) with OSC_QUERY_ENABLE clear and * the updated input and output buffers used before. Since the feature * bits that were clear in the return buffer from the previous _OSC * evaluation are also clear in the capabilities buffer now, this _OSC * evaluation is not expected to fail. */ capbuf[OSC_QUERY_DWORD] = 0; /* Reuse in_params[] populated by acpi_eval_osc(). */ input.pointer = in_params; input.count = 4; if (ACPI_FAILURE(acpi_evaluate_object(handle, "_OSC", &input, &output))) { ret = -ENODATA; goto out; } /* * Clear the feature bits in capbuf[] that have not been acknowledged. * After that, capbuf[] contains the resultant feature mask. */ for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) { test |= capbuf[i] & ~retbuf[i]; capbuf[i] &= retbuf[i]; } errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK; /* * Some platforms set OSC_CAPABILITIES_MASK_ERROR even though they * acknowledge all of the requested features, so avoid complaining in * those cases unless any other error bits are also set. */ if (errors && (test || errors != OSC_CAPABILITIES_MASK_ERROR)) { /* * Complain about the unexpected errors and print diagnostic * information related to them. */ acpi_handle_err(handle, "_OSC: errors while processing control request\n"); acpi_handle_err(handle, "_OSC: some features may be missing\n"); acpi_osc_error_check(handle, &guid, rev, &cap, retbuf); } out: ACPI_FREE(out_obj); return ret;} bool osc_sb_apei_support_acked; /* * ACPI 6.0 Section 8.4.4.2 Idle State Coordination * OSPM supports platform coordinated low power idle(LPI) states */bool osc_pc_lpi_support_confirmed;EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed); /* * ACPI 6.2 Section 6.2.11.2 'Platform-Wide OSPM Capabilities': * Starting with ACPI Specification 6.2, all _CPC registers can be in * PCC, System Memory, System IO, or Functional Fixed Hardware address * spaces. OSPM support for this more flexible register space scheme is * indicated by the “Flexible Address Space for CPPC Registers” _OSC bit. * * Otherwise (cf ACPI 6.1, s8.4.7.1.1.X), _CPC registers must be in: * - PCC or Functional Fixed Hardware address space if defined * - SystemMemory address space (NULL register) if not defined */bool osc_cpc_flexible_adr_space_confirmed;EXPORT_SYMBOL_GPL(osc_cpc_flexible_adr_space_confirmed); /* * ACPI 6.4 Operating System Capabilities for USB. */bool osc_sb_native_usb4_support_confirmed;EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); bool osc_sb_cppc2_support_acked; static void acpi_bus_osc_negotiate_platform_control(void){ static const u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; u32 capbuf[2], feature_mask; acpi_handle handle; feature_mask = OSC_SB_PR3_SUPPORT | OSC_SB_HOTPLUG_OST_SUPPORT | OSC_SB_PCLPI_SUPPORT | OSC_SB_OVER_16_PSTATES_SUPPORT | OSC_SB_GED_SUPPORT | OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; if (IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_X86)) feature_mask |= OSC_SB_GENERIC_INITIATOR_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_CPPC_LIB)) { feature_mask |= OSC_SB_CPC_SUPPORT | OSC_SB_CPCV2_SUPPORT | OSC_SB_CPC_FLEXIBLE_ADR_SPACE; if (IS_ENABLED(CONFIG_SCHED_MC_PRIO)) feature_mask |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT; } if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR)) feature_mask |= OSC_SB_PAD_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PROCESSOR)) feature_mask |= OSC_SB_PPC_OST_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_THERMAL)) feature_mask |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_BATTERY)) feature_mask |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PRMT)) feature_mask |= OSC_SB_PRM_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_FFH)) feature_mask |= OSC_SB_FFH_OPR_SUPPORT; if (IS_ENABLED(CONFIG_USB4)) feature_mask |= OSC_SB_NATIVE_USB4_SUPPORT; if (!ghes_disable) feature_mask |= OSC_SB_APEI_SUPPORT; if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return;Lines 1–500 of 1510. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * acpi_bus.c - ACPI Bus Driver ($Revision: 80 $) * * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> */ #define pr_fmt(fmt) "ACPI: " fmt #include <linux/module.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/device.h> #include <linux/proc_fs.h> #include <linux/acpi.h> #include <linux/slab.h> #include <linux/regulator/machine.h> #include <linux/workqueue.h> #include <linux/reboot.h> #include <linux/delay.h> #ifdef CONFIG_X86 #include <asm/mpspec.h> #include <linux/dmi.h> #endif #include <linux/acpi_viot.h> #include <linux/pci.h> #include <acpi/apei.h> #include <linux/suspend.h> #include <linux/prmt.h> #include "internal.h" struct acpi_device *acpi_root; struct proc_dir_entry *acpi_root_dir; EXPORT_SYMBOL(acpi_root_dir); #ifdef CONFIG_X86 #ifdef CONFIG_ACPI_CUSTOM_DSDT static inline int set_copy_dsdt(const struct dmi_system_id *id) { return 0; } #else static int set_copy_dsdt(const struct dmi_system_id *id) { pr_notice("%s detected - force copy of DSDT to local memory\n", id->ident); acpi_gbl_copy_dsdt_locally = 1; return 0; } #endif static const struct dmi_system_id dsdt_dmi_table[] __initconst = { /* * Invoke DSDT corruption work-around on all Toshiba Satellite. * https://bugzilla.kernel.org/show_bug.cgi?id=14679 */ { .callback = set_copy_dsdt, .ident = "TOSHIBA Satellite", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), }, }, {} }; #endif /* -------------------------------------------------------------------------- Device Management -------------------------------------------------------------------------- */ acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta) { acpi_status status; status = acpi_evaluate_integer(handle, "_STA", NULL, sta); if (ACPI_SUCCESS(status)) return AE_OK; if (status == AE_NOT_FOUND) { *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; return AE_OK; } return status; } EXPORT_SYMBOL_GPL(acpi_bus_get_status_handle); int acpi_bus_get_status(struct acpi_device *device) { acpi_status status; unsigned long long sta; if (acpi_device_override_status(device, &sta)) { acpi_set_device_status(device, sta); return 0; } /* Battery devices must have their deps met before calling _STA */ if (acpi_device_is_battery(device) && device->dep_unmet) { acpi_set_device_status(device, 0); return 0; } status = acpi_bus_get_status_handle(device->handle, &sta); if (ACPI_FAILURE(status)) return -ENODEV; if (!device->status.present && device->status.enabled) { pr_info(FW_BUG "Device [%s] status [%08x]: not present and enabled\n", device->pnp.bus_id, (u32)sta); device->status.enabled = 0; /* * The status is clearly invalid, so clear the functional bit as * well to avoid attempting to use the device. */ device->status.functional = 0; } acpi_set_device_status(device, sta); if (device->status.functional && !device->status.present) { pr_debug("Device [%s] status [%08x]: functional but not present\n", device->pnp.bus_id, (u32)sta); } pr_debug("Device [%s] status [%08x]\n", device->pnp.bus_id, (u32)sta); return 0; } EXPORT_SYMBOL(acpi_bus_get_status); void acpi_bus_private_data_handler(acpi_handle handle, void *context) { return; } EXPORT_SYMBOL(acpi_bus_private_data_handler); int acpi_bus_attach_private_data(acpi_handle handle, void *data) { acpi_status status; status = acpi_attach_data(handle, acpi_bus_private_data_handler, data); if (ACPI_FAILURE(status)) { acpi_handle_debug(handle, "Error attaching device data\n"); return -ENODEV; } return 0; } EXPORT_SYMBOL_GPL(acpi_bus_attach_private_data); int acpi_bus_get_private_data(acpi_handle handle, void **data) { acpi_status status; if (!data) return -EINVAL; status = acpi_get_data(handle, acpi_bus_private_data_handler, data); if (ACPI_FAILURE(status)) { acpi_handle_debug(handle, "No context for object\n"); return -ENODEV; } return 0; } EXPORT_SYMBOL_GPL(acpi_bus_get_private_data); void acpi_bus_detach_private_data(acpi_handle handle) { acpi_detach_data(handle, acpi_bus_private_data_handler); } EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); static void acpi_dump_osc_data(acpi_handle handle, const guid_t *guid, int rev, struct acpi_buffer *cap) { u32 *capbuf = cap->pointer; int i; acpi_handle_debug(handle, "_OSC: UUID: %pUL, rev: %d\n", guid, rev); for (i = 0; i < cap->length / sizeof(u32); i++) acpi_handle_debug(handle, "_OSC: capabilities DWORD %i: [%08x]\n", i, capbuf[i]); } #define OSC_ERROR_MASK (OSC_REQUEST_ERROR | OSC_INVALID_UUID_ERROR | \ OSC_INVALID_REVISION_ERROR | \ OSC_CAPABILITIES_MASK_ERROR) static int acpi_eval_osc(acpi_handle handle, guid_t *guid, int rev, struct acpi_buffer *cap, union acpi_object in_params[at_least 4], struct acpi_buffer *output) { struct acpi_object_list input; union acpi_object *out_obj; acpi_status status; in_params[0].type = ACPI_TYPE_BUFFER; in_params[0].buffer.length = sizeof(*guid); in_params[0].buffer.pointer = (u8 *)guid; in_params[1].type = ACPI_TYPE_INTEGER; in_params[1].integer.value = rev; in_params[2].type = ACPI_TYPE_INTEGER; in_params[2].integer.value = cap->length / sizeof(u32); in_params[3].type = ACPI_TYPE_BUFFER; in_params[3].buffer.length = cap->length; in_params[3].buffer.pointer = cap->pointer; input.pointer = in_params; input.count = 4; output->length = ACPI_ALLOCATE_BUFFER; output->pointer = NULL; status = acpi_evaluate_object(handle, "_OSC", &input, output); if (ACPI_FAILURE(status) || !output->length) return -ENODATA; out_obj = output->pointer; if (out_obj->type != ACPI_TYPE_BUFFER || out_obj->buffer.length != cap->length) { acpi_handle_debug(handle, "Invalid _OSC return buffer\n"); acpi_dump_osc_data(handle, guid, rev, cap); ACPI_FREE(out_obj); return -ENODATA; } return 0; } static bool acpi_osc_error_check(acpi_handle handle, guid_t *guid, int rev, struct acpi_buffer *cap, u32 *retbuf) { /* Only take defined error bits into account. */ u32 errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK; u32 *capbuf = cap->pointer; bool fail; /* * If OSC_QUERY_ENABLE is set, ignore the "capabilities masked" * bit because it merely means that some features have not been * acknowledged which is not unexpected. */ if (capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE) errors &= ~OSC_CAPABILITIES_MASK_ERROR; if (!errors) return false; acpi_dump_osc_data(handle, guid, rev, cap); /* * As a rule, fail only if OSC_QUERY_ENABLE is set because otherwise the * acknowledged features need to be controlled. */ fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); if (errors & OSC_REQUEST_ERROR) acpi_handle_debug(handle, "_OSC: request failed\n"); if (errors & OSC_INVALID_UUID_ERROR) { acpi_handle_debug(handle, "_OSC: invalid UUID\n"); /* * Always fail if this bit is set because it means that the * request could not be processed. */ fail = true; } if (errors & OSC_INVALID_REVISION_ERROR) acpi_handle_debug(handle, "_OSC: invalid revision\n"); if (errors & OSC_CAPABILITIES_MASK_ERROR) acpi_handle_debug(handle, "_OSC: capability bits masked\n"); return fail; } acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) { union acpi_object in_params[4], *out_obj; struct acpi_buffer output; acpi_status status = AE_OK; guid_t guid; u32 *retbuf; int ret; if (!context || !context->cap.pointer || context->cap.length < 2 * sizeof(u32) || guid_parse(context->uuid_str, &guid)) return AE_BAD_PARAMETER; ret = acpi_eval_osc(handle, &guid, context->rev, &context->cap, in_params, &output); if (ret) return AE_ERROR; out_obj = output.pointer; retbuf = (u32 *)out_obj->buffer.pointer; if (acpi_osc_error_check(handle, &guid, context->rev, &context->cap, retbuf)) { status = AE_ERROR; goto out; } context->ret.length = out_obj->buffer.length; context->ret.pointer = kmemdup(retbuf, context->ret.length, GFP_KERNEL); if (!context->ret.pointer) { status = AE_NO_MEMORY; goto out; } status = AE_OK; out: ACPI_FREE(out_obj); return status; } EXPORT_SYMBOL(acpi_run_osc); static int acpi_osc_handshake(acpi_handle handle, const char *uuid_str, int rev, u32 *capbuf, size_t bufsize) { union acpi_object in_params[4], *out_obj; struct acpi_object_list input; struct acpi_buffer cap = { .pointer = capbuf, .length = bufsize * sizeof(u32), }; struct acpi_buffer output; u32 *retbuf, test, errors; guid_t guid; int ret, i; if (!capbuf || bufsize < 2 || guid_parse(uuid_str, &guid)) return -EINVAL; /* First evaluate _OSC with OSC_QUERY_ENABLE set. */ capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; ret = acpi_eval_osc(handle, &guid, rev, &cap, in_params, &output); if (ret) return ret; out_obj = output.pointer; retbuf = (u32 *)out_obj->buffer.pointer; if (acpi_osc_error_check(handle, &guid, rev, &cap, retbuf)) { ret = -ENODATA; goto out; } /* * Clear the feature bits in the capabilities buffer that have not been * acknowledged and clear the return buffer. */ for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) { capbuf[i] &= retbuf[i]; test |= capbuf[i]; retbuf[i] = 0; } /* * If none of the feature bits have been acknowledged, there's nothing * more to do. capbuf[] contains a feature mask of all zeros. */ if (!test) goto out; retbuf[OSC_QUERY_DWORD] = 0; /* * Now evaluate _OSC again (directly) with OSC_QUERY_ENABLE clear and * the updated input and output buffers used before. Since the feature * bits that were clear in the return buffer from the previous _OSC * evaluation are also clear in the capabilities buffer now, this _OSC * evaluation is not expected to fail. */ capbuf[OSC_QUERY_DWORD] = 0; /* Reuse in_params[] populated by acpi_eval_osc(). */ input.pointer = in_params; input.count = 4; if (ACPI_FAILURE(acpi_evaluate_object(handle, "_OSC", &input, &output))) { ret = -ENODATA; goto out; } /* * Clear the feature bits in capbuf[] that have not been acknowledged. * After that, capbuf[] contains the resultant feature mask. */ for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) { test |= capbuf[i] & ~retbuf[i]; capbuf[i] &= retbuf[i]; } errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK; /* * Some platforms set OSC_CAPABILITIES_MASK_ERROR even though they * acknowledge all of the requested features, so avoid complaining in * those cases unless any other error bits are also set. */ if (errors && (test || errors != OSC_CAPABILITIES_MASK_ERROR)) { /* * Complain about the unexpected errors and print diagnostic * information related to them. */ acpi_handle_err(handle, "_OSC: errors while processing control request\n"); acpi_handle_err(handle, "_OSC: some features may be missing\n"); acpi_osc_error_check(handle, &guid, rev, &cap, retbuf); } out: ACPI_FREE(out_obj); return ret; } bool osc_sb_apei_support_acked; /* * ACPI 6.0 Section 8.4.4.2 Idle State Coordination * OSPM supports platform coordinated low power idle(LPI) states */ bool osc_pc_lpi_support_confirmed; EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed); /* * ACPI 6.2 Section 6.2.11.2 'Platform-Wide OSPM Capabilities': * Starting with ACPI Specification 6.2, all _CPC registers can be in * PCC, System Memory, System IO, or Functional Fixed Hardware address * spaces. OSPM support for this more flexible register space scheme is * indicated by the “Flexible Address Space for CPPC Registers” _OSC bit. * * Otherwise (cf ACPI 6.1, s8.4.7.1.1.X), _CPC registers must be in: * - PCC or Functional Fixed Hardware address space if defined * - SystemMemory address space (NULL register) if not defined */ bool osc_cpc_flexible_adr_space_confirmed; EXPORT_SYMBOL_GPL(osc_cpc_flexible_adr_space_confirmed); /* * ACPI 6.4 Operating System Capabilities for USB. */ bool osc_sb_native_usb4_support_confirmed; EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); bool osc_sb_cppc2_support_acked; static void acpi_bus_osc_negotiate_platform_control(void) { static const u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; u32 capbuf[2], feature_mask; acpi_handle handle; feature_mask = OSC_SB_PR3_SUPPORT | OSC_SB_HOTPLUG_OST_SUPPORT | OSC_SB_PCLPI_SUPPORT | OSC_SB_OVER_16_PSTATES_SUPPORT | OSC_SB_GED_SUPPORT | OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; if (IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_X86)) feature_mask |= OSC_SB_GENERIC_INITIATOR_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_CPPC_LIB)) { feature_mask |= OSC_SB_CPC_SUPPORT | OSC_SB_CPCV2_SUPPORT | OSC_SB_CPC_FLEXIBLE_ADR_SPACE; if (IS_ENABLED(CONFIG_SCHED_MC_PRIO)) feature_mask |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT; } if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR)) feature_mask |= OSC_SB_PAD_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PROCESSOR)) feature_mask |= OSC_SB_PPC_OST_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_THERMAL)) feature_mask |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_BATTERY)) feature_mask |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PRMT)) feature_mask |= OSC_SB_PRM_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_FFH)) feature_mask |= OSC_SB_FFH_OPR_SUPPORT; if (IS_ENABLED(CONFIG_USB4)) feature_mask |= OSC_SB_NATIVE_USB4_SUPPORT; if (!ghes_disable) feature_mask |= OSC_SB_APEI_SUPPORT; if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return;