From bd8191cc8a74018e255eb3efff5e02dc305a5ed1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:35:14 +0200 Subject: ACPI / property: Add routine for extraction of _DSD properties Move the extraction of _DSD properties from acpi_init_properties() to a separate routine called acpi_extract_properties() to make the subsequent changes more straightforward. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/property.c | 69 ++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 32 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 6d99450549c5..8163b4bd7a61 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -100,34 +100,13 @@ static void acpi_init_of_compatible(struct acpi_device *adev) adev->flags.of_compatible_ok = 1; } -void acpi_init_properties(struct acpi_device *adev) +static bool acpi_extract_properties(const union acpi_object *desc, + struct acpi_device_data *data) { - struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; - bool acpi_of = false; - struct acpi_hardware_id *hwid; - const union acpi_object *desc; - acpi_status status; int i; - /* - * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in - * Device Tree compatible properties for this device. - */ - list_for_each_entry(hwid, &adev->pnp.ids, list) { - if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) { - acpi_of = true; - break; - } - } - - status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf, - ACPI_TYPE_PACKAGE); - if (ACPI_FAILURE(status)) - goto out; - - desc = buf.pointer; if (desc->package.count % 2) - goto fail; + return false; /* Look for the device properties UUID. */ for (i = 0; i < desc->package.count; i += 2) { @@ -154,18 +133,44 @@ void acpi_init_properties(struct acpi_device *adev) if (!acpi_properties_format_valid(properties)) break; - adev->data.pointer = buf.pointer; - adev->data.properties = properties; + data->properties = properties; + return true; + } - if (acpi_of) - acpi_init_of_compatible(adev); + return false; +} - goto out; +void acpi_init_properties(struct acpi_device *adev) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + struct acpi_hardware_id *hwid; + acpi_status status; + bool acpi_of = false; + + /* + * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in + * Device Tree compatible properties for this device. + */ + list_for_each_entry(hwid, &adev->pnp.ids, list) { + if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) { + acpi_of = true; + break; + } } - fail: - dev_dbg(&adev->dev, "Returned _DSD data is not valid, skipping\n"); - ACPI_FREE(buf.pointer); + status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + goto out; + + if (acpi_extract_properties(buf.pointer, &adev->data)) { + adev->data.pointer = buf.pointer; + if (acpi_of) + acpi_init_of_compatible(adev); + } else { + acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); + ACPI_FREE(buf.pointer); + } out: if (acpi_of && !adev->flags.of_compatible_ok) -- cgit v1.2.3 From 445b0eb058f5f31c844a731cb82e7441d0d9e578 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:36:14 +0200 Subject: ACPI / property: Add support for data-only subnodes In some cases, the information expressed via device properties is hierarchical by nature. For example, the properties of a composite device consisting of multiple semi-dependent components may need to be represented in the form of a tree of property data sets corresponding to specific components of the device. Unfortunately, using ACPI device objects for this purpose turns out to be problematic, mostly due to the assumption made by some operating systems (that platform firmware generally needs to work with) that each device object in the ACPI namespace represents a device requiring a separate driver. That assumption leads to complications which reportedly are impractically difficult to overcome and a different approach is needed for the sake of interoperability. The approach implemented here is based on extending _DSD via pointers (links) to additional ACPI objects returning data packages formatted in accordance with the _DSD formatting rules defined by Section 6.2.5 of ACPI 6. Those additional objects are referred to as data-only subnodes of the device object containing the _DSD pointing to them. The links to them need to be located in a separate section of the _DSD data package following UUID dbb8e3e6-5886-4ba6-8795-1319f52a966b referred to as the Hierarchical Data Extension UUID as defined in [1]. Each of them is represented by a package of two strings. The first string in that package (the key) is regarded as the name of the data-only subnode pointed to by the link. The second string in it (the target) is expected to hold the ACPI namespace path (possibly utilizing the usual ACPI namespace search rules) of an ACPI object evaluating to a data package extending the _DSD. The device properties initialization code follows those links, creates a struct acpi_data_node object for each of them to store the data returned by the ACPI object pointed to by it and processes those data recursively (which may lead to the creation of more struct acpi_data_node objects if the returned data package contains the Hierarchical Data Extension UUID section with more links in it). All of the struct acpi_data_node objects are present until the the ACPI device object containing the _DSD with links to them is deleted and they are deleted along with that object. [1]: http://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.pdf Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/property.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++- include/acpi/acpi_bus.h | 9 ++++ include/linux/fwnode.h | 1 + 3 files changed, 142 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 8163b4bd7a61..17c436de376b 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -24,6 +24,115 @@ static const u8 prp_uuid[16] = { 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01 }; +/* ACPI _DSD data subnodes UUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */ +static const u8 ads_uuid[16] = { + 0xe6, 0xe3, 0xb8, 0xdb, 0x86, 0x58, 0xa6, 0x4b, + 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b +}; + +static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, + const union acpi_object *desc, + struct acpi_device_data *data); +static bool acpi_extract_properties(const union acpi_object *desc, + struct acpi_device_data *data); + +static bool acpi_nondev_subnode_ok(acpi_handle scope, + const union acpi_object *link, + struct list_head *list) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + struct acpi_data_node *dn; + acpi_handle handle; + acpi_status status; + + dn = kzalloc(sizeof(*dn), GFP_KERNEL); + if (!dn) + return false; + + dn->name = link->package.elements[0].string.pointer; + dn->fwnode.type = FWNODE_ACPI_DATA; + INIT_LIST_HEAD(&dn->data.subnodes); + + status = acpi_get_handle(scope, link->package.elements[1].string.pointer, + &handle); + if (ACPI_FAILURE(status)) + goto fail; + + status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + goto fail; + + if (acpi_extract_properties(buf.pointer, &dn->data)) + dn->data.pointer = buf.pointer; + + if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) + dn->data.pointer = buf.pointer; + + if (dn->data.pointer) { + list_add_tail(&dn->sibling, list); + return true; + } + + acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n"); + + fail: + ACPI_FREE(buf.pointer); + kfree(dn); + return false; +} + +static int acpi_add_nondev_subnodes(acpi_handle scope, + const union acpi_object *links, + struct list_head *list) +{ + bool ret = false; + int i; + + for (i = 0; i < links->package.count; i++) { + const union acpi_object *link; + + link = &links->package.elements[i]; + /* Only two elements allowed, both must be strings. */ + if (link->package.count == 2 + && link->package.elements[0].type == ACPI_TYPE_STRING + && link->package.elements[1].type == ACPI_TYPE_STRING + && acpi_nondev_subnode_ok(scope, link, list)) + ret = true; + } + + return ret; +} + +static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, + const union acpi_object *desc, + struct acpi_device_data *data) +{ + int i; + + /* Look for the ACPI data subnodes UUID. */ + for (i = 0; i < desc->package.count; i += 2) { + const union acpi_object *uuid, *links; + + uuid = &desc->package.elements[i]; + links = &desc->package.elements[i + 1]; + + /* + * The first element must be a UUID and the second one must be + * a package. + */ + if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16 + || links->type != ACPI_TYPE_PACKAGE) + break; + + if (memcmp(uuid->buffer.pointer, ads_uuid, sizeof(ads_uuid))) + continue; + + return acpi_add_nondev_subnodes(scope, links, &data->subnodes); + } + + return false; +} static bool acpi_property_value_ok(const union acpi_object *value) { @@ -147,6 +256,8 @@ void acpi_init_properties(struct acpi_device *adev) acpi_status status; bool acpi_of = false; + INIT_LIST_HEAD(&adev->data.subnodes); + /* * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in * Device Tree compatible properties for this device. @@ -167,7 +278,11 @@ void acpi_init_properties(struct acpi_device *adev) adev->data.pointer = buf.pointer; if (acpi_of) acpi_init_of_compatible(adev); - } else { + } + if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer, &adev->data)) + adev->data.pointer = buf.pointer; + + if (!adev->data.pointer) { acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); ACPI_FREE(buf.pointer); } @@ -178,8 +293,24 @@ void acpi_init_properties(struct acpi_device *adev) ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n"); } +static void acpi_destroy_nondev_subnodes(struct list_head *list) +{ + struct acpi_data_node *dn, *next; + + if (list_empty(list)) + return; + + list_for_each_entry_safe_reverse(dn, next, list, sibling) { + acpi_destroy_nondev_subnodes(&dn->data.subnodes); + list_del(&dn->sibling); + ACPI_FREE((void *)dn->data.pointer); + kfree(dn); + } +} + void acpi_free_properties(struct acpi_device *adev) { + acpi_destroy_nondev_subnodes(&adev->data.subnodes); ACPI_FREE((void *)adev->data.pointer); adev->data.of_compatible = NULL; adev->data.pointer = NULL; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5ba8fb64f664..79cfee646d6b 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -343,6 +343,7 @@ struct acpi_device_data { const union acpi_object *pointer; const union acpi_object *properties; const union acpi_object *of_compatible; + struct list_head subnodes; }; struct acpi_gpio_mapping; @@ -378,6 +379,14 @@ struct acpi_device { void (*remove)(struct acpi_device *); }; +/* Non-device subnode */ +struct acpi_data_node { + const char *name; + struct fwnode_handle fwnode; + struct acpi_device_data data; + struct list_head sibling; +}; + static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) { bool ret = false; diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 0408545bce42..b08d6ba5c1e6 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -16,6 +16,7 @@ enum fwnode_type { FWNODE_INVALID = 0, FWNODE_OF, FWNODE_ACPI, + FWNODE_ACPI_DATA, FWNODE_PDATA, }; -- cgit v1.2.3 From 263b4c1a64bc12470684aeaf7c44f03d31716819 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:37:19 +0200 Subject: ACPI / property: Expose data-only subnodes via sysfs Add infrastructure needed to expose data-only subnodes of ACPI device objects introduced previously via sysfs. Each data-only subnode is represented as a sysfs directory under the directory corresponding to its parent object (a device or a data-only subnode). Each of them has a "path" attribute (containing the full ACPI namespace path to the object the subnode data come from) at this time. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/device_sysfs.c | 120 +++++++++++++++++++++++++++++++++++++++----- drivers/acpi/property.c | 8 +-- include/acpi/acpi_bus.h | 3 ++ 3 files changed, 116 insertions(+), 15 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index 4ab4582e586b..707cf6213bc2 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -26,6 +26,106 @@ #include "internal.h" +static ssize_t acpi_object_path(acpi_handle handle, char *buf) +{ + struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; + int result; + + result = acpi_get_name(handle, ACPI_FULL_PATHNAME, &path); + if (result) + return result; + + result = sprintf(buf, "%s\n", (char*)path.pointer); + kfree(path.pointer); + return result; +} + +struct acpi_data_node_attr { + struct attribute attr; + ssize_t (*show)(struct acpi_data_node *, char *); + ssize_t (*store)(struct acpi_data_node *, const char *, size_t count); +}; + +#define DATA_NODE_ATTR(_name) \ + static struct acpi_data_node_attr data_node_##_name = \ + __ATTR(_name, 0444, data_node_show_##_name, NULL) + +static ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf) +{ + return acpi_object_path(dn->handle, buf); +} + +DATA_NODE_ATTR(path); + +static struct attribute *acpi_data_node_default_attrs[] = { + &data_node_path.attr, + NULL +}; + +#define to_data_node(k) container_of(k, struct acpi_data_node, kobj) +#define to_attr(a) container_of(a, struct acpi_data_node_attr, attr) + +static ssize_t acpi_data_node_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct acpi_data_node *dn = to_data_node(kobj); + struct acpi_data_node_attr *dn_attr = to_attr(attr); + + return dn_attr->show ? dn_attr->show(dn, buf) : -ENXIO; +} + +static const struct sysfs_ops acpi_data_node_sysfs_ops = { + .show = acpi_data_node_attr_show, +}; + +static void acpi_data_node_release(struct kobject *kobj) +{ + struct acpi_data_node *dn = to_data_node(kobj); + complete(&dn->kobj_done); +} + +static struct kobj_type acpi_data_node_ktype = { + .sysfs_ops = &acpi_data_node_sysfs_ops, + .default_attrs = acpi_data_node_default_attrs, + .release = acpi_data_node_release, +}; + +static void acpi_expose_nondev_subnodes(struct kobject *kobj, + struct acpi_device_data *data) +{ + struct list_head *list = &data->subnodes; + struct acpi_data_node *dn; + + if (list_empty(list)) + return; + + list_for_each_entry(dn, list, sibling) { + int ret; + + init_completion(&dn->kobj_done); + ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype, + kobj, dn->name); + if (ret) + acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret); + else + acpi_expose_nondev_subnodes(&dn->kobj, &dn->data); + } +} + +static void acpi_hide_nondev_subnodes(struct acpi_device_data *data) +{ + struct list_head *list = &data->subnodes; + struct acpi_data_node *dn; + + if (list_empty(list)) + return; + + list_for_each_entry_reverse(dn, list, sibling) { + acpi_hide_nondev_subnodes(&dn->data); + kobject_put(&dn->kobj); + } +} + /** * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent * @acpi_dev: ACPI device object. @@ -323,20 +423,12 @@ static ssize_t acpi_device_adr_show(struct device *dev, } static DEVICE_ATTR(adr, 0444, acpi_device_adr_show, NULL); -static ssize_t -acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { +static ssize_t acpi_device_path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; - int result; - - result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); - if (result) - goto end; - result = sprintf(buf, "%s\n", (char*)path.pointer); - kfree(path.pointer); -end: - return result; + return acpi_object_path(acpi_dev->handle, buf); } static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); @@ -475,6 +567,8 @@ int acpi_device_setup_files(struct acpi_device *dev) &dev_attr_real_power_state); } + acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data); + end: return result; } @@ -485,6 +579,8 @@ end: */ void acpi_device_remove_files(struct acpi_device *dev) { + acpi_hide_nondev_subnodes(&dev->data); + if (dev->flags.power_manageable) { device_remove_file(&dev->dev, &dev_attr_power_state); if (dev->power.flags.power_resources) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 17c436de376b..333f9146d19e 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -64,12 +64,13 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, goto fail; if (acpi_extract_properties(buf.pointer, &dn->data)) - dn->data.pointer = buf.pointer; + dn->handle = handle; if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) - dn->data.pointer = buf.pointer; + dn->handle = handle; - if (dn->data.pointer) { + if (dn->handle) { + dn->data.pointer = buf.pointer; list_add_tail(&dn->sibling, list); return true; } @@ -302,6 +303,7 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list) list_for_each_entry_safe_reverse(dn, next, list, sibling) { acpi_destroy_nondev_subnodes(&dn->data.subnodes); + wait_for_completion(&dn->kobj_done); list_del(&dn->sibling); ACPI_FREE((void *)dn->data.pointer); kfree(dn); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 79cfee646d6b..e0d7c193d6e0 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -382,9 +382,12 @@ struct acpi_device { /* Non-device subnode */ struct acpi_data_node { const char *name; + acpi_handle handle; struct fwnode_handle fwnode; struct acpi_device_data data; struct list_head sibling; + struct kobject kobj; + struct completion kobj_done; }; static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) -- cgit v1.2.3 From 3a7a2ab839ad18c2d542b40f4a647c98d068e55a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:40:05 +0200 Subject: ACPI / property: Extend fwnode_property_* to data-only subnodes Modify is_acpi_node() to return "true" for ACPI data-only subnodes as well as for ACPI device objects and change the name of to_acpi_node() to to_acpi_device_node() so it is clear that it covers ACPI device objects only. Accordingly, introduce to_acpi_data_node() to cover data-only subnodes in an analogous way. With that, make the fwnode_property_* family of functions work with ACPI data-only subnodes introduced previously. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/property.c | 138 ++++++++++++++++++++++++++++++++++++------------ drivers/base/property.c | 16 +++--- drivers/gpio/gpiolib.c | 6 +-- include/acpi/acpi_bus.h | 21 +++++++- include/linux/acpi.h | 53 +++++++++++++------ 5 files changed, 173 insertions(+), 61 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 333f9146d19e..e78551726acb 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -19,6 +19,11 @@ #include "internal.h" +static int acpi_data_get_property_array(struct acpi_device_data *data, + const char *name, + acpi_object_type type, + const union acpi_object **obj); + /* ACPI _DSD device properties UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ static const u8 prp_uuid[16] = { 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d, @@ -191,8 +196,8 @@ static void acpi_init_of_compatible(struct acpi_device *adev) const union acpi_object *of_compatible; int ret; - ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING, - &of_compatible); + ret = acpi_data_get_property_array(&adev->data, "compatible", + ACPI_TYPE_STRING, &of_compatible); if (ret) { ret = acpi_dev_get_property(adev, "compatible", ACPI_TYPE_STRING, &of_compatible); @@ -320,8 +325,8 @@ void acpi_free_properties(struct acpi_device *adev) } /** - * acpi_dev_get_property - return an ACPI property with given name - * @adev: ACPI device to get property + * acpi_data_get_property - return an ACPI property with given name + * @data: ACPI device deta object to get the property from * @name: Name of the property * @type: Expected property type * @obj: Location to store the property value (if not %NULL) @@ -330,26 +335,27 @@ void acpi_free_properties(struct acpi_device *adev) * object at the location pointed to by @obj if found. * * Callers must not attempt to free the returned objects. These objects will be - * freed by the ACPI core automatically during the removal of @adev. + * freed by the ACPI core automatically during the removal of @data. * * Return: %0 if property with @name has been found (success), * %-EINVAL if the arguments are invalid, * %-ENODATA if the property doesn't exist, * %-EPROTO if the property value type doesn't match @type. */ -int acpi_dev_get_property(struct acpi_device *adev, const char *name, - acpi_object_type type, const union acpi_object **obj) +static int acpi_data_get_property(struct acpi_device_data *data, + const char *name, acpi_object_type type, + const union acpi_object **obj) { const union acpi_object *properties; int i; - if (!adev || !name) + if (!data || !name) return -EINVAL; - if (!adev->data.pointer || !adev->data.properties) + if (!data->pointer || !data->properties) return -ENODATA; - properties = adev->data.properties; + properties = data->properties; for (i = 0; i < properties->package.count; i++) { const union acpi_object *propname, *propvalue; const union acpi_object *property; @@ -370,11 +376,50 @@ int acpi_dev_get_property(struct acpi_device *adev, const char *name, } return -ENODATA; } + +/** + * acpi_dev_get_property - return an ACPI property with given name. + * @adev: ACPI device to get the property from. + * @name: Name of the property. + * @type: Expected property type. + * @obj: Location to store the property value (if not %NULL). + */ +int acpi_dev_get_property(struct acpi_device *adev, const char *name, + acpi_object_type type, const union acpi_object **obj) +{ + return adev ? acpi_data_get_property(&adev->data, name, type, obj) : -EINVAL; +} EXPORT_SYMBOL_GPL(acpi_dev_get_property); +static struct acpi_device_data *acpi_device_data_of_node(struct fwnode_handle *fwnode) +{ + if (fwnode->type == FWNODE_ACPI) { + struct acpi_device *adev = to_acpi_device_node(fwnode); + return &adev->data; + } else if (fwnode->type == FWNODE_ACPI_DATA) { + struct acpi_data_node *dn = to_acpi_data_node(fwnode); + return &dn->data; + } + return NULL; +} + /** - * acpi_dev_get_property_array - return an ACPI array property with given name - * @adev: ACPI device to get property + * acpi_node_prop_get - return an ACPI property with given name. + * @fwnode: Firmware node to get the property from. + * @propname: Name of the property. + * @valptr: Location to store a pointer to the property value (if not %NULL). + */ +int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname, + void **valptr) +{ + return acpi_data_get_property(acpi_device_data_of_node(fwnode), + propname, ACPI_TYPE_ANY, + (const union acpi_object **)valptr); +} + +/** + * acpi_data_get_property_array - return an ACPI array property with given name + * @adev: ACPI data object to get the property from * @name: Name of the property * @type: Expected type of array elements * @obj: Location to store a pointer to the property value (if not NULL) @@ -383,7 +428,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_get_property); * ACPI object at the location pointed to by @obj if found. * * Callers must not attempt to free the returned objects. Those objects will be - * freed by the ACPI core automatically during the removal of @adev. + * freed by the ACPI core automatically during the removal of @data. * * Return: %0 if array property (package) with @name has been found (success), * %-EINVAL if the arguments are invalid, @@ -391,14 +436,15 @@ EXPORT_SYMBOL_GPL(acpi_dev_get_property); * %-EPROTO if the property is not a package or the type of its elements * doesn't match @type. */ -int acpi_dev_get_property_array(struct acpi_device *adev, const char *name, - acpi_object_type type, - const union acpi_object **obj) +static int acpi_data_get_property_array(struct acpi_device_data *data, + const char *name, + acpi_object_type type, + const union acpi_object **obj) { const union acpi_object *prop; int ret, i; - ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &prop); + ret = acpi_data_get_property(data, name, ACPI_TYPE_PACKAGE, &prop); if (ret) return ret; @@ -413,7 +459,6 @@ int acpi_dev_get_property_array(struct acpi_device *adev, const char *name, return 0; } -EXPORT_SYMBOL_GPL(acpi_dev_get_property_array); /** * acpi_dev_get_property_reference - returns handle to the referenced object @@ -518,15 +563,9 @@ int acpi_dev_get_property_reference(struct acpi_device *adev, } EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference); -int acpi_dev_prop_get(struct acpi_device *adev, const char *propname, - void **valptr) -{ - return acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, - (const union acpi_object **)valptr); -} - -int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, - enum dev_prop_type proptype, void *val) +static int acpi_data_prop_read_single(struct acpi_device_data *data, + const char *propname, + enum dev_prop_type proptype, void *val) { const union acpi_object *obj; int ret; @@ -535,7 +574,7 @@ int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, return -EINVAL; if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) { - ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_INTEGER, &obj); + ret = acpi_data_get_property(data, propname, ACPI_TYPE_INTEGER, &obj); if (ret) return ret; @@ -560,7 +599,7 @@ int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, break; } } else if (proptype == DEV_PROP_STRING) { - ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_STRING, &obj); + ret = acpi_data_get_property(data, propname, ACPI_TYPE_STRING, &obj); if (ret) return ret; @@ -571,6 +610,12 @@ int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, return ret; } +int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val) +{ + return adev ? acpi_data_prop_read_single(&adev->data, propname, proptype, val) : -EINVAL; +} + static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val, size_t nval) { @@ -647,20 +692,22 @@ static int acpi_copy_property_array_string(const union acpi_object *items, return 0; } -int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, - enum dev_prop_type proptype, void *val, size_t nval) +static int acpi_data_prop_read(struct acpi_device_data *data, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) { const union acpi_object *obj; const union acpi_object *items; int ret; if (val && nval == 1) { - ret = acpi_dev_prop_read_single(adev, propname, proptype, val); + ret = acpi_data_prop_read_single(data, propname, proptype, val); if (!ret) return ret; } - ret = acpi_dev_get_property_array(adev, propname, ACPI_TYPE_ANY, &obj); + ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj); if (ret) return ret; @@ -696,3 +743,28 @@ int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, } return ret; } + +int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval) +{ + return adev ? acpi_data_prop_read(&adev->data, propname, proptype, val, nval) : -EINVAL; +} + +/** + * acpi_node_prop_read - retrieve the value of an ACPI property with given name. + * @fwnode: Firmware node to get the property from. + * @propname: Name of the property. + * @proptype: Expected property type. + * @val: Location to store the property value (if not %NULL). + * @nval: Size of the array pointed to by @val. + * + * If @val is %NULL, return the number of array elements comprising the value + * of the property. Otherwise, read at most @nval values to the array at the + * location pointed to by @val. + */ +int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval) +{ + return acpi_data_prop_read(acpi_device_data_of_node(fwnode), + propname, proptype, val, nval); +} diff --git a/drivers/base/property.c b/drivers/base/property.c index 2d75366c61e0..ca118169a6c5 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -134,7 +134,7 @@ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) if (is_of_node(fwnode)) return of_property_read_bool(to_of_node(fwnode), propname); else if (is_acpi_node(fwnode)) - return !acpi_dev_prop_get(to_acpi_node(fwnode), propname, NULL); + return !acpi_node_prop_get(fwnode, propname, NULL); return !!pset_prop_get(to_pset(fwnode), propname); } @@ -298,8 +298,8 @@ EXPORT_SYMBOL_GPL(device_property_read_string); _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \ _type_, _val_, _nval_); \ else if (is_acpi_node(_fwnode_)) \ - _ret_ = acpi_dev_prop_read(to_acpi_node(_fwnode_), _propname_, \ - _proptype_, _val_, _nval_); \ + _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \ + _val_, _nval_); \ else if (is_pset(_fwnode_)) \ _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ _proptype_, _val_, _nval_); \ @@ -440,8 +440,8 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, propname, val, nval) : of_property_count_strings(to_of_node(fwnode), propname); else if (is_acpi_node(fwnode)) - return acpi_dev_prop_read(to_acpi_node(fwnode), propname, - DEV_PROP_STRING, val, nval); + return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, + val, nval); else if (is_pset(fwnode)) return pset_prop_read_array(to_pset(fwnode), propname, DEV_PROP_STRING, val, nval); @@ -470,8 +470,8 @@ int fwnode_property_read_string(struct fwnode_handle *fwnode, if (is_of_node(fwnode)) return of_property_read_string(to_of_node(fwnode), propname, val); else if (is_acpi_node(fwnode)) - return acpi_dev_prop_read(to_acpi_node(fwnode), propname, - DEV_PROP_STRING, val, 1); + return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, + val, 1); return pset_prop_read_array(to_pset(fwnode), propname, DEV_PROP_STRING, val, 1); @@ -495,7 +495,7 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, } else if (IS_ENABLED(CONFIG_ACPI)) { struct acpi_device *node; - node = acpi_get_next_child(dev, to_acpi_node(child)); + node = acpi_get_next_child(dev, to_acpi_device_node(child)); if (node) return acpi_fwnode_handle(node); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 980c1f87866a..f43e808a49d9 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2083,11 +2083,11 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, &flags); if (!IS_ERR(desc)) active_low = flags & OF_GPIO_ACTIVE_LOW; - } else if (is_acpi_node(fwnode)) { + } else if (is_acpi_device_node(fwnode)) { struct acpi_gpio_info info; - desc = acpi_get_gpiod_by_index(to_acpi_node(fwnode), propname, 0, - &info); + desc = acpi_get_gpiod_by_index(to_acpi_device_node(fwnode), + propname, 0, &info); if (!IS_ERR(desc)) active_low = info.active_low; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index e0d7c193d6e0..e234725eadc7 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -424,16 +424,33 @@ static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) } static inline bool is_acpi_node(struct fwnode_handle *fwnode) +{ + return fwnode && (fwnode->type == FWNODE_ACPI + || fwnode->type == FWNODE_ACPI_DATA); +} + +static inline bool is_acpi_device_node(struct fwnode_handle *fwnode) { return fwnode && fwnode->type == FWNODE_ACPI; } -static inline struct acpi_device *to_acpi_node(struct fwnode_handle *fwnode) +static inline struct acpi_device *to_acpi_device_node(struct fwnode_handle *fwnode) { - return is_acpi_node(fwnode) ? + return is_acpi_device_node(fwnode) ? container_of(fwnode, struct acpi_device, fwnode) : NULL; } +static inline bool is_acpi_data_node(struct fwnode_handle *fwnode) +{ + return fwnode && fwnode->type == FWNODE_ACPI_DATA; +} + +static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwnode) +{ + return is_acpi_data_node(fwnode) ? + container_of(fwnode, struct acpi_data_node, fwnode) : NULL; +} + static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) { return &adev->fwnode; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7235c4851460..6be94ba4e980 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -49,7 +49,7 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) return adev ? adev->handle : NULL; } -#define ACPI_COMPANION(dev) to_acpi_node((dev)->fwnode) +#define ACPI_COMPANION(dev) to_acpi_device_node((dev)->fwnode) #define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \ acpi_fwnode_handle(adev) : NULL) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) @@ -69,7 +69,7 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) static inline bool has_acpi_companion(struct device *dev) { - return is_acpi_node(dev->fwnode); + return is_acpi_device_node(dev->fwnode); } static inline void acpi_preset_companion(struct device *dev, @@ -461,7 +461,22 @@ static inline bool is_acpi_node(struct fwnode_handle *fwnode) return false; } -static inline struct acpi_device *to_acpi_node(struct fwnode_handle *fwnode) +static inline bool is_acpi_device_node(struct fwnode_handle *fwnode) +{ + return false; +} + +static inline struct acpi_device *to_acpi_device_node(struct fwnode_handle *fwnode) +{ + return NULL; +} + +static inline bool is_acpi_data_node(struct fwnode_handle *fwnode) +{ + return false; +} + +static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwnode) { return NULL; } @@ -743,17 +758,16 @@ struct acpi_reference_args { #ifdef CONFIG_ACPI int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, const union acpi_object **obj); -int acpi_dev_get_property_array(struct acpi_device *adev, const char *name, - acpi_object_type type, - const union acpi_object **obj); int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name, size_t index, struct acpi_reference_args *args); -int acpi_dev_prop_get(struct acpi_device *adev, const char *propname, - void **valptr); +int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname, + void **valptr); int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val); +int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval); int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val, size_t nval); @@ -766,13 +780,7 @@ static inline int acpi_dev_get_property(struct acpi_device *adev, { return -ENXIO; } -static inline int acpi_dev_get_property_array(struct acpi_device *adev, - const char *name, - acpi_object_type type, - const union acpi_object **obj) -{ - return -ENXIO; -} + static inline int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name, const char *cells_name, size_t index, struct acpi_reference_args *args) @@ -780,6 +788,13 @@ static inline int acpi_dev_get_property_reference(struct acpi_device *adev, return -ENXIO; } +static inline int acpi_node_prop_get(struct fwnode_handle *fwnode, + const char *propname, + void **valptr) +{ + return -ENXIO; +} + static inline int acpi_dev_prop_get(struct acpi_device *adev, const char *propname, void **valptr) @@ -795,6 +810,14 @@ static inline int acpi_dev_prop_read_single(struct acpi_device *adev, return -ENXIO; } +static inline int acpi_node_prop_read(struct fwnode_handle *fwnode, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) +{ + return -ENXIO; +} + static inline int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, -- cgit v1.2.3 From 504a33749971c36c54ba5ccb1364872dee1f17a7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:42:33 +0200 Subject: ACPI / property: Extend device_get_next_child_node() to data-only nodes Make device_get_next_child_node() work with ACPI data-only subnodes introduced previously. Namely, replace acpi_get_next_child() with acpi_get_next_subnode() that can handle (and return) child device objects as well as child data-only subnodes of the given device and modify the ACPI part of the GPIO subsystem to handle data-only subnodes returned by it. To that end, introduce acpi_node_get_gpiod() taking a struct fwnode_handle pointer as the first argument. That argument may point to an ACPI device object as well as to a data-only subnode and the function should do the right thing (ie. look for the matching GPIO descriptor correctly) in either case. Next, modify fwnode_get_named_gpiod() to use acpi_node_get_gpiod() instead of acpi_get_gpiod_by_index() which automatically causes devm_get_gpiod_from_child() to work with ACPI data-only subnodes that may be returned by device_get_next_child_node() which in turn is required by the users of that function (the gpio_keys_polled and gpio-leds drivers). Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg Acked-by: Linus Walleij --- drivers/acpi/property.c | 88 ++++++++++++++++++++++++++++++++++++++++----- drivers/acpi/scan.c | 20 ----------- drivers/base/property.c | 6 +--- drivers/gpio/gpiolib-acpi.c | 58 +++++++++++++++++++++++++++--- drivers/gpio/gpiolib.c | 5 ++- drivers/gpio/gpiolib.h | 10 +++++- include/linux/acpi.h | 17 +++++---- 7 files changed, 153 insertions(+), 51 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index e78551726acb..14654435c295 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -461,9 +461,9 @@ static int acpi_data_get_property_array(struct acpi_device_data *data, } /** - * acpi_dev_get_property_reference - returns handle to the referenced object - * @adev: ACPI device to get property - * @name: Name of the property + * acpi_data_get_property_reference - returns handle to the referenced object + * @data: ACPI device data object containing the property + * @propname: Name of the property * @index: Index of the reference to return * @args: Location to store the returned reference with optional arguments * @@ -477,16 +477,16 @@ static int acpi_data_get_property_array(struct acpi_device_data *data, * * Return: %0 on success, negative error code on failure. */ -int acpi_dev_get_property_reference(struct acpi_device *adev, - const char *name, size_t index, - struct acpi_reference_args *args) +static int acpi_data_get_property_reference(struct acpi_device_data *data, + const char *propname, size_t index, + struct acpi_reference_args *args) { const union acpi_object *element, *end; const union acpi_object *obj; struct acpi_device *device; int ret, idx = 0; - ret = acpi_dev_get_property(adev, name, ACPI_TYPE_ANY, &obj); + ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj); if (ret) return ret; @@ -561,7 +561,23 @@ int acpi_dev_get_property_reference(struct acpi_device *adev, return -EPROTO; } -EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference); + +/** + * acpi_node_get_property_reference - get a handle to the referenced object. + * @fwnode: Firmware node to get the property from. + * @propname: Name of the property. + * @index: Index of the reference to return. + * @args: Location to store the returned reference with optional arguments. + */ +int acpi_node_get_property_reference(struct fwnode_handle *fwnode, + const char *name, size_t index, + struct acpi_reference_args *args) +{ + struct acpi_device_data *data = acpi_device_data_of_node(fwnode); + + return data ? acpi_data_get_property_reference(data, name, index, args) : -EINVAL; +} +EXPORT_SYMBOL_GPL(acpi_node_get_property_reference); static int acpi_data_prop_read_single(struct acpi_device_data *data, const char *propname, @@ -768,3 +784,59 @@ int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, return acpi_data_prop_read(acpi_device_data_of_node(fwnode), propname, proptype, val, nval); } + +/** + * acpi_get_next_subnode - Return the next child node handle for a device. + * @dev: Device to find the next child node for. + * @child: Handle to one of the device's child nodes or a null handle. + */ +struct fwnode_handle *acpi_get_next_subnode(struct device *dev, + struct fwnode_handle *child) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + struct list_head *head, *next; + + if (!adev) + return NULL; + + if (!child || child->type == FWNODE_ACPI) { + head = &adev->children; + if (list_empty(head)) + goto nondev; + + if (child) { + adev = to_acpi_device_node(child); + next = adev->node.next; + if (next == head) { + child = NULL; + goto nondev; + } + adev = list_entry(next, struct acpi_device, node); + } else { + adev = list_first_entry(head, struct acpi_device, node); + } + return acpi_fwnode_handle(adev); + } + + nondev: + if (!child || child->type == FWNODE_ACPI_DATA) { + struct acpi_data_node *dn; + + head = &adev->data.subnodes; + if (list_empty(head)) + return NULL; + + if (child) { + dn = to_acpi_data_node(child); + next = dn->sibling.next; + if (next == head) + return NULL; + + dn = list_entry(next, struct acpi_data_node, sibling); + } else { + dn = list_first_entry(head, struct acpi_data_node, sibling); + } + return &dn->fwnode; + } + return NULL; +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 01136b879038..d1ce377db3e9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -695,26 +695,6 @@ int acpi_device_add(struct acpi_device *device, return result; } -struct acpi_device *acpi_get_next_child(struct device *dev, - struct acpi_device *child) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - struct list_head *head, *next; - - if (!adev) - return NULL; - - head = &adev->children; - if (list_empty(head)) - return NULL; - - if (!child) - return list_first_entry(head, struct acpi_device, node); - - next = child->node.next; - return next == head ? NULL : list_entry(next, struct acpi_device, node); -} - /* -------------------------------------------------------------------------- Device Enumeration -------------------------------------------------------------------------- */ diff --git a/drivers/base/property.c b/drivers/base/property.c index ca118169a6c5..660ecec60bdf 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -493,11 +493,7 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, if (node) return &node->fwnode; } else if (IS_ENABLED(CONFIG_ACPI)) { - struct acpi_device *node; - - node = acpi_get_next_child(dev, to_acpi_device_node(child)); - if (node) - return acpi_fwnode_handle(node); + return acpi_get_next_subnode(dev, child); } return NULL; } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index a38cf16a5828..69a83626f1ae 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -453,7 +453,7 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, return 0; } -static int acpi_gpio_property_lookup(struct acpi_device *adev, +static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, const char *propname, int index, struct acpi_gpio_lookup *lookup) { @@ -461,10 +461,16 @@ static int acpi_gpio_property_lookup(struct acpi_device *adev, int ret; memset(&args, 0, sizeof(args)); - ret = acpi_dev_get_property_reference(adev, propname, index, &args); - if (ret && !acpi_get_driver_gpio_data(adev, propname, index, &args)) - return ret; + ret = acpi_node_get_property_reference(fwnode, propname, index, &args); + if (ret) { + struct acpi_device *adev = to_acpi_device_node(fwnode); + if (!adev) + return ret; + + if (!acpi_get_driver_gpio_data(adev, propname, index, &args)) + return ret; + } /* * The property was found and resolved, so need to lookup the GPIO based * on returned args. @@ -518,7 +524,8 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, if (propname) { dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); - ret = acpi_gpio_property_lookup(adev, propname, index, &lookup); + ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), + propname, index, &lookup); if (ret) return ERR_PTR(ret); @@ -534,6 +541,47 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, return ret ? ERR_PTR(ret) : lookup.desc; } +/** + * acpi_node_get_gpiod() - get a GPIO descriptor from ACPI resources + * @fwnode: pointer to an ACPI firmware node to get the GPIO information from + * @propname: Property name of the GPIO + * @index: index of GpioIo/GpioInt resource (starting from %0) + * @info: info pointer to fill in (optional) + * + * If @fwnode is an ACPI device object, call %acpi_get_gpiod_by_index() for it. + * Otherwise (ie. it is a data-only non-device object), use the property-based + * GPIO lookup to get to the GPIO resource with the relevant information and use + * that to obtain the GPIO descriptor to return. + */ +struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, + const char *propname, int index, + struct acpi_gpio_info *info) +{ + struct acpi_gpio_lookup lookup; + struct acpi_device *adev; + int ret; + + adev = to_acpi_device_node(fwnode); + if (adev) + return acpi_get_gpiod_by_index(adev, propname, index, info); + + if (!is_acpi_data_node(fwnode)) + return ERR_PTR(-ENODEV); + + if (!propname) + return ERR_PTR(-EINVAL); + + memset(&lookup, 0, sizeof(lookup)); + lookup.index = index; + + ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); + if (ret) + return ERR_PTR(ret); + + ret = acpi_gpio_resource_lookup(&lookup, info); + return ret ? ERR_PTR(ret) : lookup.desc; +} + /** * acpi_dev_gpio_irq_get() - Find GpioInt and translate it to Linux IRQ number * @adev: pointer to a ACPI device to get IRQ from diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f43e808a49d9..7d61b506c42f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2083,11 +2083,10 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, &flags); if (!IS_ERR(desc)) active_low = flags & OF_GPIO_ACTIVE_LOW; - } else if (is_acpi_device_node(fwnode)) { + } else if (is_acpi_node(fwnode)) { struct acpi_gpio_info info; - desc = acpi_get_gpiod_by_index(to_acpi_device_node(fwnode), - propname, 0, &info); + desc = acpi_node_get_gpiod(fwnode, propname, 0, &info); if (!IS_ERR(desc)) active_low = info.active_low; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index bf343004b008..e69c7157cdad 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -42,6 +42,9 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, int index, struct acpi_gpio_info *info); +struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, + const char *propname, int index, + struct acpi_gpio_info *info); int acpi_gpio_count(struct device *dev, const char *con_id); #else @@ -60,7 +63,12 @@ acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, { return ERR_PTR(-ENOSYS); } - +static inline struct gpio_desc * +acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, + int index, struct acpi_gpio_info *info) +{ + return ERR_PTR(-ENXIO); +} static inline int acpi_gpio_count(struct device *dev, const char *con_id) { return -ENODEV; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6be94ba4e980..865d948c60e6 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -758,9 +758,9 @@ struct acpi_reference_args { #ifdef CONFIG_ACPI int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, const union acpi_object **obj); -int acpi_dev_get_property_reference(struct acpi_device *adev, - const char *name, size_t index, - struct acpi_reference_args *args); +int acpi_node_get_property_reference(struct fwnode_handle *fwnode, + const char *name, size_t index, + struct acpi_reference_args *args); int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname, void **valptr); @@ -771,8 +771,8 @@ int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val, size_t nval); -struct acpi_device *acpi_get_next_child(struct device *dev, - struct acpi_device *child); +struct fwnode_handle *acpi_get_next_subnode(struct device *dev, + struct fwnode_handle *subnode); #else static inline int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, @@ -781,7 +781,7 @@ static inline int acpi_dev_get_property(struct acpi_device *adev, return -ENXIO; } -static inline int acpi_dev_get_property_reference(struct acpi_device *adev, +static inline int acpi_node_get_property_reference(struct fwnode_handle *fwnode, const char *name, const char *cells_name, size_t index, struct acpi_reference_args *args) { @@ -826,12 +826,11 @@ static inline int acpi_dev_prop_read(struct acpi_device *adev, return -ENXIO; } -static inline struct acpi_device *acpi_get_next_child(struct device *dev, - struct acpi_device *child) +static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev, + struct fwnode_handle *subnode) { return NULL; } - #endif #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3 From 636c19d38920caee61d694ef306d110d33935038 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 9 Sep 2015 23:59:41 +0200 Subject: ACPI / scan: constify first argument of struct acpi_scan_handler::match One wouldn't expect a "match" function modify the string it searches for, and indeed the only instance of the struct acpi_scan_handler::match callback, acpi_pnp_match, can easily be changed. While there, update its helper matching_id(). This is also preparation for constifying struct acpi_hardware_id::id. Signed-off-by: Rasmus Villemoes Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_pnp.c | 4 ++-- include/acpi/acpi_bus.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index c58940b231d6..48fc3ad13a4b 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -316,7 +316,7 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {""}, }; -static bool matching_id(char *idstr, char *list_id) +static bool matching_id(const char *idstr, const char *list_id) { int i; @@ -333,7 +333,7 @@ static bool matching_id(char *idstr, char *list_id) return true; } -static bool acpi_pnp_match(char *idstr, const struct acpi_device_id **matchid) +static bool acpi_pnp_match(const char *idstr, const struct acpi_device_id **matchid) { const struct acpi_device_id *devid; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5ba8fb64f664..9a1b46c64fc1 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -129,7 +129,7 @@ static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; - bool (*match)(char *idstr, const struct acpi_device_id **matchid); + bool (*match)(const char *idstr, const struct acpi_device_id **matchid); int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); void (*detach)(struct acpi_device *dev); void (*bind)(struct device *phys_dev); -- cgit v1.2.3 From 844142c3f80c66fb2c311b118d60abdfe02322cb Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 9 Sep 2015 23:59:42 +0200 Subject: ACPI / scan: constify struct acpi_hardware_id::id This is preparation for using kstrdup_const to initialize that member. Signed-off-by: Rasmus Villemoes Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 4 ++-- drivers/pnp/pnpacpi/core.c | 4 ++-- include/acpi/acpi_bus.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 01136b879038..a3eaf2080707 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1472,7 +1472,7 @@ bool acpi_device_is_present(struct acpi_device *adev) } static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, - char *idstr, + const char *idstr, const struct acpi_device_id **matchid) { const struct acpi_device_id *devid; @@ -1491,7 +1491,7 @@ static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, return false; } -static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, +static struct acpi_scan_handler *acpi_scan_match_handler(const char *idstr, const struct acpi_device_id **matchid) { struct acpi_scan_handler *handler; diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 5153d1d69aee..9113876487ed 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -207,7 +207,7 @@ struct pnp_protocol pnpacpi_protocol = { }; EXPORT_SYMBOL(pnpacpi_protocol); -static char *__init pnpacpi_get_id(struct acpi_device *device) +static const char *__init pnpacpi_get_id(struct acpi_device *device) { struct acpi_hardware_id *id; @@ -222,7 +222,7 @@ static char *__init pnpacpi_get_id(struct acpi_device *device) static int __init pnpacpi_add_device(struct acpi_device *device) { struct pnp_dev *dev; - char *pnpid; + const char *pnpid; struct acpi_hardware_id *id; int error; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9a1b46c64fc1..fec002919b65 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -227,7 +227,7 @@ typedef char acpi_device_class[20]; struct acpi_hardware_id { struct list_head list; - char *id; + const char *id; }; struct acpi_pnp_type { -- cgit v1.2.3 From 6a0d12ef993c5291047d72e88d73d0aa2b967335 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 9 Sep 2015 23:59:43 +0200 Subject: ACPI / scan: use kstrdup_const() in acpi_add_id() Empirically, acpi_add_id is mostly called with string literals, so using kstrdup_const for initializing struct acpi_hardware_id::id saves a little run-time memory and a string copy. Signed-off-by: Rasmus Villemoes Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a3eaf2080707..afaac47eefb4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1184,7 +1184,7 @@ static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id) if (!id) return; - id->id = kstrdup(dev_id, GFP_KERNEL); + id->id = kstrdup_const(dev_id, GFP_KERNEL); if (!id->id) { kfree(id); return; @@ -1322,7 +1322,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp) struct acpi_hardware_id *id, *tmp; list_for_each_entry_safe(id, tmp, &pnp->ids, list) { - kfree(id->id); + kfree_const(id->id); kfree(id); } kfree(pnp->unique_id); -- cgit v1.2.3 From c33cab60bc0e05ddf1ef1b2cba8d16dd010f600a Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Thu, 10 Sep 2015 23:48:09 +0300 Subject: ACPI: change init_acpi_device_notify() to return void This patch changes the type of the return value of the init_acpi_device_notify() method to be void, as this method never fails and its return value is never used. Signed-off-by: Rami Rosen Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 5 ++--- drivers/acpi/internal.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index b9657af751d1..1470ae4f98c0 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -351,13 +351,12 @@ static int acpi_platform_notify_remove(struct device *dev) return 0; } -int __init init_acpi_device_notify(void) +void __init init_acpi_device_notify(void) { if (platform_notify || platform_notify_remove) { printk(KERN_ERR PREFIX "Can't use platform_notify\n"); - return 0; + return; } platform_notify = acpi_platform_notify; platform_notify_remove = acpi_platform_notify_remove; - return 0; } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 9e426210c2a8..e72c6a67201f 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -21,7 +21,7 @@ #define PREFIX "ACPI: " acpi_status acpi_os_initialize1(void); -int init_acpi_device_notify(void); +void init_acpi_device_notify(void); int acpi_scan_init(void); void acpi_pci_root_init(void); void acpi_pci_link_init(void); -- cgit v1.2.3 From f934c745079ea3a15e8619350faf681dad42cc91 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Fri, 11 Sep 2015 17:49:22 +0300 Subject: ACPI: change acpi_sleep_proc_init() to return void This patch changes the type of the return value of the acpi_sleep_proc_init() method to be void, as this method never fails and its return value is never used. Signed-off-by: Rami Rosen \ [ rjw : Fixed up the static inline stub ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 4 ++-- drivers/acpi/proc.c | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e72c6a67201f..c31787bef2d3 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -179,13 +179,13 @@ static inline int acpi_sleep_init(void) { return -ENXIO; } #endif #ifdef CONFIG_ACPI_SLEEP -int acpi_sleep_proc_init(void); +void acpi_sleep_proc_init(void); int suspend_nvs_alloc(void); void suspend_nvs_free(void); int suspend_nvs_save(void); void suspend_nvs_restore(void); #else -static inline int acpi_sleep_proc_init(void) { return 0; } +static inline void acpi_sleep_proc_init(void) {} static inline int suspend_nvs_alloc(void) { return 0; } static inline void suspend_nvs_free(void) {} static inline int suspend_nvs_save(void) { return 0; } diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 75c28eae8860..2a358154b770 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -144,11 +144,9 @@ static const struct file_operations acpi_system_wakeup_device_fops = { .release = single_release, }; -int __init acpi_sleep_proc_init(void) +void __init acpi_sleep_proc_init(void) { /* 'wakeup device' [R/W] */ proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, acpi_root_dir, &acpi_system_wakeup_device_fops); - - return 0; } -- cgit v1.2.3 From 0700c047f66101c7d0d23e3583f1ae9c3c2dd71c Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 24 Sep 2015 14:54:48 +0800 Subject: ACPI / EC: Fix query handler related issues 1. acpi_ec_remove_query_handlers() This patch refines the query handler removal logic implemented in acpi_ec_remove_query_handler(), making it to invoke new acpi_ec_remove_query_handlers() API, and ensuring all other removal code paths to invoke the new API to honor the reference count of the query handlers. 2. acpi_ec_get_query_handler_by_value() This patch also refines the query handler search logic originally implemented in acpi_ec_query(), collecting it into acpi_ec_get_query_handler_by_value(). And since schedule_work() can ensure the serilization of acpi_ec_event_handler(), we needn't put the mutex_lock() around schedule_work(). Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 73 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 28 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 42c66b64c12c..2db0912bb2e6 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -929,6 +929,23 @@ acpi_ec_get_query_handler(struct acpi_ec_query_handler *handler) return handler; } +static struct acpi_ec_query_handler * +acpi_ec_get_query_handler_by_value(struct acpi_ec *ec, u8 value) +{ + struct acpi_ec_query_handler *handler; + bool found = false; + + mutex_lock(&ec->mutex); + list_for_each_entry(handler, &ec->list, node) { + if (value == handler->query_bit) { + found = true; + break; + } + } + mutex_unlock(&ec->mutex); + return found ? acpi_ec_get_query_handler(handler) : NULL; +} + static void acpi_ec_query_handler_release(struct kref *kref) { struct acpi_ec_query_handler *handler = @@ -964,14 +981,15 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, } EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); -void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) +static void acpi_ec_remove_query_handlers(struct acpi_ec *ec, + bool remove_all, u8 query_bit) { struct acpi_ec_query_handler *handler, *tmp; LIST_HEAD(free_list); mutex_lock(&ec->mutex); list_for_each_entry_safe(handler, tmp, &ec->list, node) { - if (query_bit == handler->query_bit) { + if (remove_all || query_bit == handler->query_bit) { list_del_init(&handler->node); list_add(&handler->node, &free_list); } @@ -980,6 +998,11 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) list_for_each_entry_safe(handler, tmp, &free_list, node) acpi_ec_put_query_handler(handler); } + +void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) +{ + acpi_ec_remove_query_handlers(ec, false, query_bit); +} EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); static struct acpi_ec_query *acpi_ec_create_query(u8 *pval) @@ -1025,7 +1048,6 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) { u8 value = 0; int result; - struct acpi_ec_query_handler *handler; struct acpi_ec_query *q; q = acpi_ec_create_query(&value); @@ -1043,25 +1065,26 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) if (result) goto err_exit; - mutex_lock(&ec->mutex); - result = -ENODATA; - list_for_each_entry(handler, &ec->list, node) { - if (value == handler->query_bit) { - result = 0; - q->handler = acpi_ec_get_query_handler(handler); - ec_dbg_evt("Query(0x%02x) scheduled", - q->handler->query_bit); - /* - * It is reported that _Qxx are evaluated in a - * parallel way on Windows: - * https://bugzilla.kernel.org/show_bug.cgi?id=94411 - */ - if (!schedule_work(&q->work)) - result = -EBUSY; - break; - } + q->handler = acpi_ec_get_query_handler_by_value(ec, value); + if (!q->handler) { + result = -ENODATA; + goto err_exit; + } + + /* + * It is reported that _Qxx are evaluated in a parallel way on + * Windows: + * https://bugzilla.kernel.org/show_bug.cgi?id=94411 + * + * Put this log entry before schedule_work() in order to make + * it appearing before any other log entries occurred during the + * work queue execution. + */ + ec_dbg_evt("Query(0x%02x) scheduled", value); + if (!schedule_work(&q->work)) { + ec_dbg_evt("Query(0x%02x) overlapped", value); + result = -EBUSY; } - mutex_unlock(&ec->mutex); err_exit: if (result && q) @@ -1354,19 +1377,13 @@ static int acpi_ec_add(struct acpi_device *device) static int acpi_ec_remove(struct acpi_device *device) { struct acpi_ec *ec; - struct acpi_ec_query_handler *handler, *tmp; if (!device) return -EINVAL; ec = acpi_driver_data(device); ec_remove_handlers(ec); - mutex_lock(&ec->mutex); - list_for_each_entry_safe(handler, tmp, &ec->list, node) { - list_del(&handler->node); - kfree(handler); - } - mutex_unlock(&ec->mutex); + acpi_ec_remove_query_handlers(ec, true, 0); release_region(ec->data_addr, 1); release_region(ec->command_addr, 1); device->driver_data = NULL; -- cgit v1.2.3 From 611975470740b38084610e0a64ff4c6ff5aec89a Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 24 Sep 2015 14:54:54 +0800 Subject: ACPI / EC: Fix a race issue in acpi_ec_guard_event() In acpi_ec_guard_event(), EC transaction state machine variables should be checked with the EC spinlock locked. The bug doesn't trigger any real issue now because this bug can only occur when the ec_event_clearing=event mode is applied while there is no user currently using this mode. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 2db0912bb2e6..f61a7c834540 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -441,17 +441,31 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) static bool acpi_ec_guard_event(struct acpi_ec *ec) { + bool guarded = true; + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + /* + * If firmware SCI_EVT clearing timing is "event", we actually + * don't know when the SCI_EVT will be cleared by firmware after + * evaluating _Qxx, so we need to re-check SCI_EVT after waiting an + * acceptable period. + * + * The guarding period begins when EC_FLAGS_QUERY_PENDING is + * flagged, which means SCI_EVT check has just been performed. + * But if the current transaction is ACPI_EC_COMMAND_QUERY, the + * guarding should have already been performed (via + * EC_FLAGS_QUERY_GUARDING) and should not be applied so that the + * ACPI_EC_COMMAND_QUERY transaction can be transitioned into + * ACPI_EC_COMMAND_POLL state immediately. + */ if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY || !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) || (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY)) - return false; - - /* - * Postpone the query submission to allow the firmware to proceed, - * we shouldn't check SCI_EVT before the firmware reflagging it. - */ - return true; + guarded = false; + spin_unlock_irqrestore(&ec->lock, flags); + return guarded; } static int ec_transaction_polled(struct acpi_ec *ec) @@ -597,6 +611,7 @@ static int ec_guard(struct acpi_ec *ec) unsigned long guard = usecs_to_jiffies(ec_polling_guard); unsigned long timeout = ec->timestamp + guard; + /* Ensure guarding period before polling EC status */ do { if (ec_busy_polling) { /* Perform busy polling */ @@ -606,11 +621,13 @@ static int ec_guard(struct acpi_ec *ec) } else { /* * Perform wait polling - * - * For SCI_EVT clearing timing of "event", - * performing guarding before re-checking the - * SCI_EVT. Otherwise, such guarding is not needed - * due to the old practices. + * 1. Wait the transaction to be completed by the + * GPE handler after the transaction enters + * ACPI_EC_COMMAND_POLL state. + * 2. A special guarding logic is also required + * for event clearing mode "event" before the + * transaction enters ACPI_EC_COMMAND_POLL + * state. */ if (!ec_transaction_polled(ec) && !acpi_ec_guard_event(ec)) @@ -620,7 +637,6 @@ static int ec_guard(struct acpi_ec *ec) guard)) return 0; } - /* Guard the register accesses for the polling modes */ } while (time_before(jiffies, timeout)); return -ETIME; } -- cgit v1.2.3 From e647b532275bb357e87272e052fccf5fcdb36a17 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:12 +0100 Subject: ACPI: Add early device probing infrastructure IRQ controllers and timers are the two types of device the kernel requires before being able to use the device driver model. ACPI so far lacks a proper probing infrastructure similar to the one we have with DT, where we're able to declare IRQ chips and clocksources inside the driver code, and let the core code pick it up and call us back on a match. This leads to all kind of really ugly hacks all over the arm64 code and even in the ACPI layer. In order to allow some basic probing based on the ACPI tables, introduce "struct acpi_probe_entry" which contains just enough data and callbacks to match a table, an optional subtable, and call a probe function. A driver can, at build time, register itself and expect being called if the right entry exists in the ACPI table. A acpi_probe_device_table() is provided, taking an identifier for a set of acpi_prove_entries, and iterating over the registered entries. Signed-off-by: Marc Zyngier Reviewed-by: Lorenzo Pieralisi Reviewed-by: Hanjun Guo Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 39 +++++++++++++++++++++++ include/asm-generic/vmlinux.lds.h | 10 ++++++ include/linux/acpi.h | 66 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d1ce377db3e9..1d6b55a6f501 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1913,3 +1913,42 @@ int __init acpi_scan_init(void) mutex_unlock(&acpi_scan_lock); return result; } + +static struct acpi_probe_entry *ape; +static int acpi_probe_count; +static DEFINE_SPINLOCK(acpi_probe_lock); + +static int __init acpi_match_madt(struct acpi_subtable_header *header, + const unsigned long end) +{ + if (!ape->subtable_valid || ape->subtable_valid(header, ape)) + if (!ape->probe_subtbl(header, end)) + acpi_probe_count++; + + return 0; +} + +int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr) +{ + int count = 0; + + if (acpi_disabled) + return 0; + + spin_lock(&acpi_probe_lock); + for (ape = ap_head; nr; ape++, nr--) { + if (ACPI_COMPARE_NAME(ACPI_SIG_MADT, ape->id)) { + acpi_probe_count = 0; + acpi_table_parse_madt(ape->type, acpi_match_madt, 0); + count += acpi_probe_count; + } else { + int res; + res = acpi_table_parse(ape->id, ape->probe_table); + if (!res) + count++; + } + } + spin_unlock(&acpi_probe_lock); + + return count; +} diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 1781e54ea6d3..efd7ed1eafae 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -181,6 +181,16 @@ #define CPUIDLE_METHOD_OF_TABLES() OF_TABLE(CONFIG_CPU_IDLE, cpuidle_method) #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) +#ifdef CONFIG_ACPI +#define ACPI_PROBE_TABLE(name) \ + . = ALIGN(8); \ + VMLINUX_SYMBOL(__##name##_acpi_probe_table) = .; \ + *(__##name##_acpi_probe_table) \ + VMLINUX_SYMBOL(__##name##_acpi_probe_table_end) = .; +#else +#define ACPI_PROBE_TABLE(name) +#endif + #define KERNEL_DTB() \ STRUCT_ALIGN(); \ VMLINUX_SYMBOL(__dtb_start) = .; \ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 865d948c60e6..815f5f62513f 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -773,6 +773,61 @@ int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, struct fwnode_handle *acpi_get_next_subnode(struct device *dev, struct fwnode_handle *subnode); + +struct acpi_probe_entry; +typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *, + struct acpi_probe_entry *); + +#define ACPI_TABLE_ID_LEN 5 + +/** + * struct acpi_probe_entry - boot-time probing entry + * @id: ACPI table name + * @type: Optional subtable type to match + * (if @id contains subtables) + * @subtable_valid: Optional callback to check the validity of + * the subtable + * @probe_table: Callback to the driver being probed when table + * match is successful + * @probe_subtbl: Callback to the driver being probed when table and + * subtable match (and optional callback is successful) + * @driver_data: Sideband data provided back to the driver + */ +struct acpi_probe_entry { + __u8 id[ACPI_TABLE_ID_LEN]; + __u8 type; + acpi_probe_entry_validate_subtbl subtable_valid; + union { + acpi_tbl_table_handler probe_table; + acpi_tbl_entry_handler probe_subtbl; + }; + kernel_ulong_t driver_data; +}; + +#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, valid, data, fn) \ + static const struct acpi_probe_entry __acpi_probe_##name \ + __used __section(__##table##_acpi_probe_table) \ + = { \ + .id = table_id, \ + .type = subtable, \ + .subtable_valid = valid, \ + .probe_table = (acpi_tbl_table_handler)fn, \ + .driver_data = data, \ + } + +#define ACPI_PROBE_TABLE(name) __##name##_acpi_probe_table +#define ACPI_PROBE_TABLE_END(name) __##name##_acpi_probe_table_end + +int __acpi_probe_device_table(struct acpi_probe_entry *start, int nr); + +#define acpi_probe_device_table(t) \ + ({ \ + extern struct acpi_probe_entry ACPI_PROBE_TABLE(t), \ + ACPI_PROBE_TABLE_END(t); \ + __acpi_probe_device_table(&ACPI_PROBE_TABLE(t), \ + (&ACPI_PROBE_TABLE_END(t) - \ + &ACPI_PROBE_TABLE(t))); \ + }) #else static inline int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, @@ -831,6 +886,17 @@ static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev, { return NULL; } + +#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, validate, data, fn) \ + static const void * __acpi_table_##name[] \ + __attribute__((unused)) \ + = { (void *) table_id, \ + (void *) subtable, \ + (void *) valid, \ + (void *) fn, \ + (void *) data } + +#define acpi_probe_device_table(t) ({ int __r = 0; __r;}) #endif #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3 From 337aadff8e4567e39669e07d9a88b789d78458b5 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Fri, 2 Oct 2015 10:01:19 -0400 Subject: ACPI: Introduce CPU performance controls using CPPC CPPC stands for Collaborative Processor Performance Controls and is defined in the ACPI v5.0+ spec. It describes CPU performance controls on an abstract and continuous scale allowing the platform (e.g. remote power processor) to flexibly optimize CPU performance with its knowledge of power budgets and other architecture specific knowledge. This patch adds a shim which exports commonly used functions to get and set CPPC specific controls for each CPU. This enables CPUFreq drivers to gather per CPU performance data and use with exisiting governors or even allows for customized governors which are implemented inside CPUFreq drivers. Signed-off-by: Ashwin Chaugule Reviewed-by: Al Stone Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 14 + drivers/acpi/Makefile | 1 + drivers/acpi/cppc_acpi.c | 734 +++++++++++++++++++++++++++++++++++++++++++++++ include/acpi/cppc_acpi.h | 138 +++++++++ 4 files changed, 887 insertions(+) create mode 100644 drivers/acpi/cppc_acpi.c create mode 100644 include/acpi/cppc_acpi.h (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5d1015c26ff4..93a04eec0174 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -197,6 +197,20 @@ config ACPI_PROCESSOR_IDLE bool select CPU_IDLE +config ACPI_CPPC_LIB + bool + depends on ACPI_PROCESSOR + depends on !ACPI_CPU_FREQ_PSS + select MAILBOX + select PCC + help + If this option is enabled, this file implements common functionality + to parse CPPC tables as described in the ACPI 5.1+ spec. The + routines implemented are meant to be used by other + drivers to control CPU performance using CPPC semantics. + If your platform does not support CPPC in firmware, + leave this option disabled. + config ACPI_PROCESSOR tristate "Processor" depends on X86 || IA64 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index b5e7cd8a9c71..675eaf337178 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o +obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c new file mode 100644 index 000000000000..0bbf84bdcdc4 --- /dev/null +++ b/drivers/acpi/cppc_acpi.c @@ -0,0 +1,734 @@ +/* + * CPPC (Collaborative Processor Performance Control) methods used by CPUfreq drivers. + * + * (C) Copyright 2014, 2015 Linaro Ltd. + * Author: Ashwin Chaugule + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * CPPC describes a few methods for controlling CPU performance using + * information from a per CPU table called CPC. This table is described in + * the ACPI v5.0+ specification. The table consists of a list of + * registers which may be memory mapped or hardware registers and also may + * include some static integer values. + * + * CPU performance is on an abstract continuous scale as against a discretized + * P-state scale which is tied to CPU frequency only. In brief, the basic + * operation involves: + * + * - OS makes a CPU performance request. (Can provide min and max bounds) + * + * - Platform (such as BMC) is free to optimize request within requested bounds + * depending on power/thermal budgets etc. + * + * - Platform conveys its decision back to OS + * + * The communication between OS and platform occurs through another medium + * called (PCC) Platform Communication Channel. This is a generic mailbox like + * mechanism which includes doorbell semantics to indicate register updates. + * See drivers/mailbox/pcc.c for details on PCC. + * + * Finer details about the PCC and CPPC spec are available in the ACPI v5.1 and + * above specifications. + */ + +#define pr_fmt(fmt) "ACPI CPPC: " fmt + +#include +#include + +#include +/* + * Lock to provide mutually exclusive access to the PCC + * channel. e.g. When the remote updates the shared region + * with new data, the reader needs to be protected from + * other CPUs activity on the same channel. + */ +static DEFINE_SPINLOCK(pcc_lock); + +/* + * The cpc_desc structure contains the ACPI register details + * as described in the per CPU _CPC tables. The details + * include the type of register (e.g. PCC, System IO, FFH etc.) + * and destination addresses which lets us READ/WRITE CPU performance + * information using the appropriate I/O methods. + */ +static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); + +/* This layer handles all the PCC specifics for CPPC. */ +static struct mbox_chan *pcc_channel; +static void __iomem *pcc_comm_addr; +static u64 comm_base_addr; +static int pcc_subspace_idx = -1; +static u16 pcc_cmd_delay; +static bool pcc_channel_acquired; + +/* + * Arbitrary Retries in case the remote processor is slow to respond + * to PCC commands. + */ +#define NUM_RETRIES 500 + +static int send_pcc_cmd(u16 cmd) +{ + int retries, result = -EIO; + struct acpi_pcct_hw_reduced *pcct_ss = pcc_channel->con_priv; + struct acpi_pcct_shared_memory *generic_comm_base = + (struct acpi_pcct_shared_memory *) pcc_comm_addr; + u32 cmd_latency = pcct_ss->latency; + + /* Min time OS should wait before sending next command. */ + udelay(pcc_cmd_delay); + + /* Write to the shared comm region. */ + writew(cmd, &generic_comm_base->command); + + /* Flip CMD COMPLETE bit */ + writew(0, &generic_comm_base->status); + + /* Ring doorbell */ + result = mbox_send_message(pcc_channel, &cmd); + if (result < 0) { + pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n", + cmd, result); + return result; + } + + /* Wait for a nominal time to let platform process command. */ + udelay(cmd_latency); + + /* Retry in case the remote processor was too slow to catch up. */ + for (retries = NUM_RETRIES; retries > 0; retries--) { + if (readw_relaxed(&generic_comm_base->status) & PCC_CMD_COMPLETE) { + result = 0; + break; + } + } + + mbox_client_txdone(pcc_channel, result); + return result; +} + +static void cppc_chan_tx_done(struct mbox_client *cl, void *msg, int ret) +{ + if (ret) + pr_debug("TX did not complete: CMD sent:%x, ret:%d\n", + *(u16 *)msg, ret); + else + pr_debug("TX completed. CMD sent:%x, ret:%d\n", + *(u16 *)msg, ret); +} + +struct mbox_client cppc_mbox_cl = { + .tx_done = cppc_chan_tx_done, + .knows_txdone = true, +}; + +static int acpi_get_psd(struct cpc_desc *cpc_ptr, acpi_handle handle) +{ + int result = -EFAULT; + acpi_status status = AE_OK; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"}; + struct acpi_buffer state = {0, NULL}; + union acpi_object *psd = NULL; + struct acpi_psd_package *pdomain; + + status = acpi_evaluate_object_typed(handle, "_PSD", NULL, &buffer, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return -ENODEV; + + psd = buffer.pointer; + if (!psd || psd->package.count != 1) { + pr_debug("Invalid _PSD data\n"); + goto end; + } + + pdomain = &(cpc_ptr->domain_info); + + state.length = sizeof(struct acpi_psd_package); + state.pointer = pdomain; + + status = acpi_extract_package(&(psd->package.elements[0]), + &format, &state); + if (ACPI_FAILURE(status)) { + pr_debug("Invalid _PSD data for CPU:%d\n", cpc_ptr->cpu_id); + goto end; + } + + if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) { + pr_debug("Unknown _PSD:num_entries for CPU:%d\n", cpc_ptr->cpu_id); + goto end; + } + + if (pdomain->revision != ACPI_PSD_REV0_REVISION) { + pr_debug("Unknown _PSD:revision for CPU: %d\n", cpc_ptr->cpu_id); + goto end; + } + + if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && + pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && + pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { + pr_debug("Invalid _PSD:coord_type for CPU:%d\n", cpc_ptr->cpu_id); + goto end; + } + + result = 0; +end: + kfree(buffer.pointer); + return result; +} + +/** + * acpi_get_psd_map - Map the CPUs in a common freq domain. + * @all_cpu_data: Ptrs to CPU specific CPPC data including PSD info. + * + * Return: 0 for success or negative value for err. + */ +int acpi_get_psd_map(struct cpudata **all_cpu_data) +{ + int count_target; + int retval = 0; + unsigned int i, j; + cpumask_var_t covered_cpus; + struct cpudata *pr, *match_pr; + struct acpi_psd_package *pdomain; + struct acpi_psd_package *match_pdomain; + struct cpc_desc *cpc_ptr, *match_cpc_ptr; + + if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) + return -ENOMEM; + + /* + * Now that we have _PSD data from all CPUs, lets setup P-state + * domain info. + */ + for_each_possible_cpu(i) { + pr = all_cpu_data[i]; + if (!pr) + continue; + + if (cpumask_test_cpu(i, covered_cpus)) + continue; + + cpc_ptr = per_cpu(cpc_desc_ptr, i); + if (!cpc_ptr) + continue; + + pdomain = &(cpc_ptr->domain_info); + cpumask_set_cpu(i, pr->shared_cpu_map); + cpumask_set_cpu(i, covered_cpus); + if (pdomain->num_processors <= 1) + continue; + + /* Validate the Domain info */ + count_target = pdomain->num_processors; + if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) + pr->shared_type = CPUFREQ_SHARED_TYPE_ALL; + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) + pr->shared_type = CPUFREQ_SHARED_TYPE_HW; + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) + pr->shared_type = CPUFREQ_SHARED_TYPE_ANY; + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_cpc_ptr = per_cpu(cpc_desc_ptr, j); + if (!match_cpc_ptr) + continue; + + match_pdomain = &(match_cpc_ptr->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* Here i and j are in the same domain */ + if (match_pdomain->num_processors != count_target) { + retval = -EFAULT; + goto err_ret; + } + + if (pdomain->coord_type != match_pdomain->coord_type) { + retval = -EFAULT; + goto err_ret; + } + + cpumask_set_cpu(j, covered_cpus); + cpumask_set_cpu(j, pr->shared_cpu_map); + } + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = all_cpu_data[j]; + if (!match_pr) + continue; + + match_cpc_ptr = per_cpu(cpc_desc_ptr, j); + if (!match_cpc_ptr) + continue; + + match_pdomain = &(match_cpc_ptr->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + match_pr->shared_type = pr->shared_type; + cpumask_copy(match_pr->shared_cpu_map, + pr->shared_cpu_map); + } + } + +err_ret: + for_each_possible_cpu(i) { + pr = all_cpu_data[i]; + if (!pr) + continue; + + /* Assume no coordination on any error parsing domain info */ + if (retval) { + cpumask_clear(pr->shared_cpu_map); + cpumask_set_cpu(i, pr->shared_cpu_map); + pr->shared_type = CPUFREQ_SHARED_TYPE_ALL; + } + } + + free_cpumask_var(covered_cpus); + return retval; +} +EXPORT_SYMBOL_GPL(acpi_get_psd_map); + +static int register_pcc_channel(unsigned pcc_subspace_idx) +{ + struct acpi_pcct_subspace *cppc_ss; + unsigned int len; + + if (pcc_subspace_idx >= 0) { + pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl, + pcc_subspace_idx); + + if (IS_ERR(pcc_channel)) { + pr_err("Failed to find PCC communication channel\n"); + return -ENODEV; + } + + /* + * The PCC mailbox controller driver should + * have parsed the PCCT (global table of all + * PCC channels) and stored pointers to the + * subspace communication region in con_priv. + */ + cppc_ss = pcc_channel->con_priv; + + if (!cppc_ss) { + pr_err("No PCC subspace found for CPPC\n"); + return -ENODEV; + } + + /* + * This is the shared communication region + * for the OS and Platform to communicate over. + */ + comm_base_addr = cppc_ss->base_address; + len = cppc_ss->length; + pcc_cmd_delay = cppc_ss->min_turnaround_time; + + pcc_comm_addr = acpi_os_ioremap(comm_base_addr, len); + if (!pcc_comm_addr) { + pr_err("Failed to ioremap PCC comm region mem\n"); + return -ENOMEM; + } + + /* Set flag so that we dont come here for each CPU. */ + pcc_channel_acquired = true; + } + + return 0; +} + +/* + * An example CPC table looks like the following. + * + * Name(_CPC, Package() + * { + * 17, + * NumEntries + * 1, + * // Revision + * ResourceTemplate(){Register(PCC, 32, 0, 0x120, 2)}, + * // Highest Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x124, 2)}, + * // Nominal Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x128, 2)}, + * // Lowest Nonlinear Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x12C, 2)}, + * // Lowest Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x130, 2)}, + * // Guaranteed Performance Register + * ResourceTemplate(){Register(PCC, 32, 0, 0x110, 2)}, + * // Desired Performance Register + * ResourceTemplate(){Register(SystemMemory, 0, 0, 0, 0)}, + * .. + * .. + * .. + * + * } + * Each Register() encodes how to access that specific register. + * e.g. a sample PCC entry has the following encoding: + * + * Register ( + * PCC, + * AddressSpaceKeyword + * 8, + * //RegisterBitWidth + * 8, + * //RegisterBitOffset + * 0x30, + * //RegisterAddress + * 9 + * //AccessSize (subspace ID) + * 0 + * ) + * } + */ + +/** + * acpi_cppc_processor_probe - Search for per CPU _CPC objects. + * @pr: Ptr to acpi_processor containing this CPUs logical Id. + * + * Return: 0 for success or negative value for err. + */ +int acpi_cppc_processor_probe(struct acpi_processor *pr) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *out_obj, *cpc_obj; + struct cpc_desc *cpc_ptr; + struct cpc_reg *gas_t; + acpi_handle handle = pr->handle; + unsigned int num_ent, i, cpc_rev; + acpi_status status; + int ret = -EFAULT; + + /* Parse the ACPI _CPC table for this cpu. */ + status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto out_buf_free; + } + + out_obj = (union acpi_object *) output.pointer; + + cpc_ptr = kzalloc(sizeof(struct cpc_desc), GFP_KERNEL); + if (!cpc_ptr) { + ret = -ENOMEM; + goto out_buf_free; + } + + /* First entry is NumEntries. */ + cpc_obj = &out_obj->package.elements[0]; + if (cpc_obj->type == ACPI_TYPE_INTEGER) { + num_ent = cpc_obj->integer.value; + } else { + pr_debug("Unexpected entry type(%d) for NumEntries\n", + cpc_obj->type); + goto out_free; + } + + /* Only support CPPCv2. Bail otherwise. */ + if (num_ent != CPPC_NUM_ENT) { + pr_debug("Firmware exports %d entries. Expected: %d\n", + num_ent, CPPC_NUM_ENT); + goto out_free; + } + + /* Second entry should be revision. */ + cpc_obj = &out_obj->package.elements[1]; + if (cpc_obj->type == ACPI_TYPE_INTEGER) { + cpc_rev = cpc_obj->integer.value; + } else { + pr_debug("Unexpected entry type(%d) for Revision\n", + cpc_obj->type); + goto out_free; + } + + if (cpc_rev != CPPC_REV) { + pr_debug("Firmware exports revision:%d. Expected:%d\n", + cpc_rev, CPPC_REV); + goto out_free; + } + + /* Iterate through remaining entries in _CPC */ + for (i = 2; i < num_ent; i++) { + cpc_obj = &out_obj->package.elements[i]; + + if (cpc_obj->type == ACPI_TYPE_INTEGER) { + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; + cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value; + } else if (cpc_obj->type == ACPI_TYPE_BUFFER) { + gas_t = (struct cpc_reg *) + cpc_obj->buffer.pointer; + + /* + * The PCC Subspace index is encoded inside + * the CPC table entries. The same PCC index + * will be used for all the PCC entries, + * so extract it only once. + */ + if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { + if (pcc_subspace_idx < 0) + pcc_subspace_idx = gas_t->access_width; + else if (pcc_subspace_idx != gas_t->access_width) { + pr_debug("Mismatched PCC ids.\n"); + goto out_free; + } + } else if (gas_t->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) { + /* Support only PCC and SYS MEM type regs */ + pr_debug("Unsupported register type: %d\n", gas_t->space_id); + goto out_free; + } + + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; + memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); + } else { + pr_debug("Err in entry:%d in CPC table of CPU:%d \n", i, pr->id); + goto out_free; + } + } + /* Store CPU Logical ID */ + cpc_ptr->cpu_id = pr->id; + + /* Plug it into this CPUs CPC descriptor. */ + per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr; + + /* Parse PSD data for this CPU */ + ret = acpi_get_psd(cpc_ptr, handle); + if (ret) + goto out_free; + + /* Register PCC channel once for all CPUs. */ + if (!pcc_channel_acquired) { + ret = register_pcc_channel(pcc_subspace_idx); + if (ret) + goto out_free; + } + + /* Everything looks okay */ + pr_debug("Parsed CPC struct for CPU: %d\n", pr->id); + + kfree(output.pointer); + return 0; + +out_free: + cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); + kfree(cpc_ptr); + +out_buf_free: + kfree(output.pointer); + return ret; +} +EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe); + +/** + * acpi_cppc_processor_exit - Cleanup CPC structs. + * @pr: Ptr to acpi_processor containing this CPUs logical Id. + * + * Return: Void + */ +void acpi_cppc_processor_exit(struct acpi_processor *pr) +{ + struct cpc_desc *cpc_ptr; + cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); + kfree(cpc_ptr); +} +EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); + +static u64 get_phys_addr(struct cpc_reg *reg) +{ + /* PCC communication addr space begins at byte offset 0x8. */ + if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) + return (u64)comm_base_addr + 0x8 + reg->address; + else + return reg->address; +} + +static void cpc_read(struct cpc_reg *reg, u64 *val) +{ + u64 addr = get_phys_addr(reg); + + acpi_os_read_memory((acpi_physical_address)addr, + val, reg->bit_width); +} + +static void cpc_write(struct cpc_reg *reg, u64 val) +{ + u64 addr = get_phys_addr(reg); + + acpi_os_write_memory((acpi_physical_address)addr, + val, reg->bit_width); +} + +/** + * cppc_get_perf_caps - Get a CPUs performance capabilities. + * @cpunum: CPU from which to get capabilities info. + * @perf_caps: ptr to cppc_perf_caps. See cppc_acpi.h + * + * Return: 0 for success with perf_caps populated else -ERRNO. + */ +int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf, + *nom_perf; + u64 high, low, ref, nom; + int ret = 0; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpunum); + return -ENODEV; + } + + highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF]; + lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF]; + ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF]; + nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF]; + + spin_lock(&pcc_lock); + + /* Are any of the regs PCC ?*/ + if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + /* Ring doorbell once to update PCC subspace */ + if (send_pcc_cmd(CMD_READ)) { + ret = -EIO; + goto out_err; + } + } + + cpc_read(&highest_reg->cpc_entry.reg, &high); + perf_caps->highest_perf = high; + + cpc_read(&lowest_reg->cpc_entry.reg, &low); + perf_caps->lowest_perf = low; + + cpc_read(&ref_perf->cpc_entry.reg, &ref); + perf_caps->reference_perf = ref; + + cpc_read(&nom_perf->cpc_entry.reg, &nom); + perf_caps->nominal_perf = nom; + + if (!ref) + perf_caps->reference_perf = perf_caps->nominal_perf; + + if (!high || !low || !nom) + ret = -EFAULT; + +out_err: + spin_unlock(&pcc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(cppc_get_perf_caps); + +/** + * cppc_get_perf_ctrs - Read a CPUs performance feedback counters. + * @cpunum: CPU from which to read counters. + * @perf_fb_ctrs: ptr to cppc_perf_fb_ctrs. See cppc_acpi.h + * + * Return: 0 for success with perf_fb_ctrs populated else -ERRNO. + */ +int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + struct cpc_register_resource *delivered_reg, *reference_reg; + u64 delivered, reference; + int ret = 0; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpunum); + return -ENODEV; + } + + delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR]; + reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR]; + + spin_lock(&pcc_lock); + + /* Are any of the regs PCC ?*/ + if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + /* Ring doorbell once to update PCC subspace */ + if (send_pcc_cmd(CMD_READ)) { + ret = -EIO; + goto out_err; + } + } + + cpc_read(&delivered_reg->cpc_entry.reg, &delivered); + cpc_read(&reference_reg->cpc_entry.reg, &reference); + + if (!delivered || !reference) { + ret = -EFAULT; + goto out_err; + } + + perf_fb_ctrs->delivered = delivered; + perf_fb_ctrs->reference = reference; + + perf_fb_ctrs->delivered -= perf_fb_ctrs->prev_delivered; + perf_fb_ctrs->reference -= perf_fb_ctrs->prev_reference; + + perf_fb_ctrs->prev_delivered = delivered; + perf_fb_ctrs->prev_reference = reference; + +out_err: + spin_unlock(&pcc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs); + +/** + * cppc_set_perf - Set a CPUs performance controls. + * @cpu: CPU for which to set performance controls. + * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h + * + * Return: 0 for success, -ERRNO otherwise. + */ +int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + struct cpc_register_resource *desired_reg; + int ret = 0; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpu); + return -ENODEV; + } + + desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; + + spin_lock(&pcc_lock); + + /* + * Skip writing MIN/MAX until Linux knows how to come up with + * useful values. + */ + cpc_write(&desired_reg->cpc_entry.reg, perf_ctrls->desired_perf); + + /* Is this a PCC reg ?*/ + if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { + /* Ring doorbell so Remote can get our perf request. */ + if (send_pcc_cmd(CMD_WRITE)) + ret = -EIO; + } + + spin_unlock(&pcc_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(cppc_set_perf); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h new file mode 100644 index 000000000000..717a29810473 --- /dev/null +++ b/include/acpi/cppc_acpi.h @@ -0,0 +1,138 @@ +/* + * CPPC (Collaborative Processor Performance Control) methods used + * by CPUfreq drivers. + * + * (C) Copyright 2014, 2015 Linaro Ltd. + * Author: Ashwin Chaugule + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#ifndef _CPPC_ACPI_H +#define _CPPC_ACPI_H + +#include +#include +#include +#include + +#include + +/* Only support CPPCv2 for now. */ +#define CPPC_NUM_ENT 21 +#define CPPC_REV 2 + +#define PCC_CMD_COMPLETE 1 +#define MAX_CPC_REG_ENT 19 + +/* CPPC specific PCC commands. */ +#define CMD_READ 0 +#define CMD_WRITE 1 + +/* Each register has the folowing format. */ +struct cpc_reg { + u8 descriptor; + u16 length; + u8 space_id; + u8 bit_width; + u8 bit_offset; + u8 access_width; + u64 __iomem address; +} __packed; + +/* + * Each entry in the CPC table is either + * of type ACPI_TYPE_BUFFER or + * ACPI_TYPE_INTEGER. + */ +struct cpc_register_resource { + acpi_object_type type; + union { + struct cpc_reg reg; + u64 int_value; + } cpc_entry; +}; + +/* Container to hold the CPC details for each CPU */ +struct cpc_desc { + int num_entries; + int version; + int cpu_id; + struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT]; + struct acpi_psd_package domain_info; +}; + +/* These are indexes into the per-cpu cpc_regs[]. Order is important. */ +enum cppc_regs { + HIGHEST_PERF, + NOMINAL_PERF, + LOW_NON_LINEAR_PERF, + LOWEST_PERF, + GUARANTEED_PERF, + DESIRED_PERF, + MIN_PERF, + MAX_PERF, + PERF_REDUC_TOLERANCE, + TIME_WINDOW, + CTR_WRAP_TIME, + REFERENCE_CTR, + DELIVERED_CTR, + PERF_LIMITED, + ENABLE, + AUTO_SEL_ENABLE, + AUTO_ACT_WINDOW, + ENERGY_PERF, + REFERENCE_PERF, +}; + +/* + * Categorization of registers as described + * in the ACPI v.5.1 spec. + * XXX: Only filling up ones which are used by governors + * today. + */ +struct cppc_perf_caps { + u32 highest_perf; + u32 nominal_perf; + u32 reference_perf; + u32 lowest_perf; +}; + +struct cppc_perf_ctrls { + u32 max_perf; + u32 min_perf; + u32 desired_perf; +}; + +struct cppc_perf_fb_ctrs { + u64 reference; + u64 prev_reference; + u64 delivered; + u64 prev_delivered; +}; + +/* Per CPU container for runtime CPPC management. */ +struct cpudata { + int cpu; + struct cppc_perf_caps perf_caps; + struct cppc_perf_ctrls perf_ctrls; + struct cppc_perf_fb_ctrs perf_fb_ctrs; + struct cpufreq_policy *cur_policy; + unsigned int shared_type; + cpumask_var_t shared_cpu_map; +}; + +extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs); +extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls); +extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); +extern int acpi_get_psd_map(struct cpudata **); + +/* Methods to interact with the PCC mailbox controller. */ +extern struct mbox_chan * + pcc_mbox_request_channel(struct mbox_client *, unsigned int); +extern int mbox_send_message(struct mbox_chan *chan, void *mssg); + +#endif /* _CPPC_ACPI_H*/ -- cgit v1.2.3 From 56cdc2a05c248c9dfe2480813fc34a4e6d7a77db Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Wed, 9 Sep 2015 16:27:06 -0400 Subject: ACPI: Add weak routines for ACPI CPU Hotplug Add weak functions for architectures which do not support hot-adding and removing CPUs which aren't detected at bootup. (e.g. via MADT). This helps preserve the Kconfig dependency from: commit cbfc1bae55bb ("[ACPI] ACPI_HOTPLUG_CPU Kconfig dependency update") prevent: HOTPLUG_CPU=y ACPI_PROCESSOR=y ACPI_HOTPLUG_CPU=n Signed-off-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_processor.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 985b8a83184e..6979186dbd4b 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -164,6 +164,24 @@ static int acpi_processor_errata(void) -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_HOTPLUG_CPU +int __weak acpi_map_cpu(acpi_handle handle, + phys_cpuid_t physid, int *pcpu) +{ + return -ENODEV; +} + +int __weak acpi_unmap_cpu(int cpu) +{ + return -ENODEV; +} + +int __weak arch_register_cpu(int cpu) +{ + return -ENODEV; +} + +void __weak arch_unregister_cpu(int cpu) {} + static int acpi_processor_hotadd_init(struct acpi_processor *pr) { unsigned long long sta; -- cgit v1.2.3 From 4f2f757351df44c7109a7b93653bed9b8fe10e6b Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Wed, 9 Sep 2015 16:27:07 -0400 Subject: CPPC: Probe for CPPC tables for each ACPI Processor object For each detected ACPI Processor object (ACPI0007), search its device handle for CPPC specific tables (i.e. _CPC) and extract CPU specific performance capabilities. Signed-off-by: Ashwin Chaugule Reviewed-by: Al Stone Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_driver.c | 6 ++++++ include/acpi/processor.h | 14 ++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 51e658f21e95..f4e02ae93f58 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -242,6 +242,10 @@ static int __acpi_processor_start(struct acpi_device *device) if (pr->flags.need_hotplug_init) return 0; + result = acpi_cppc_processor_probe(pr); + if (result) + return -ENODEV; + if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) acpi_processor_power_init(pr); @@ -287,6 +291,8 @@ static int acpi_processor_stop(struct device *dev) acpi_pss_perf_exit(pr, device); + acpi_cppc_processor_exit(pr); + return 0; } diff --git a/include/acpi/processor.h b/include/acpi/processor.h index ff5f135f16b1..07fb100bcc68 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -311,6 +311,20 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle, int type, u32 acpi_id); int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id); int acpi_get_cpuid(acpi_handle, int type, u32 acpi_id); +#ifdef CONFIG_ACPI_CPPC_LIB +extern int acpi_cppc_processor_probe(struct acpi_processor *pr); +extern void acpi_cppc_processor_exit(struct acpi_processor *pr); +#else +static inline int acpi_cppc_processor_probe(struct acpi_processor *pr) +{ + return 0; +} +static inline void acpi_cppc_processor_exit(struct acpi_processor *pr) +{ + return; +} +#endif /* CONFIG_ACPI_CPPC_LIB */ + /* in processor_pdc.c */ void acpi_processor_set_pdc(acpi_handle handle); -- cgit v1.2.3 From ad806ea66c7cdfa10152c58aa710c9c26ca0d159 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Wed, 9 Sep 2015 16:27:08 -0400 Subject: ACPI: Allow selection of the ACPI processor driver for ARM64 Now that the ACPI processor driver has been decoupled from the C states and P states functionality, make it selectable on ARM64 so that it can be used by others e.g. CPPC. The C states and P states code is selected only on X86 or IA64 until the relevant support is added on ARM64. Signed-off-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 93a04eec0174..ce97a59c133e 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -213,9 +213,9 @@ config ACPI_CPPC_LIB config ACPI_PROCESSOR tristate "Processor" - depends on X86 || IA64 - select ACPI_PROCESSOR_IDLE - select ACPI_CPU_FREQ_PSS + depends on X86 || IA64 || ARM64 + select ACPI_PROCESSOR_IDLE if X86 || IA64 + select ACPI_CPU_FREQ_PSS if X86 || IA64 default y help This driver adds support for the ACPI Processor package. It is required -- cgit v1.2.3 From ef25ba0476015908ef5960f9faac149ddf34ede0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Oct 2015 00:49:34 +0200 Subject: PM / sleep: Add flags to indicate platform firmware involvement There are quite a few cases in which device drivers, bus types or even the PM core itself may benefit from knowing whether or not the platform firmware will be involved in the upcoming system power transition (during system suspend) or whether or not it was involved in it (during system resume). For this reason, introduce global system suspend flags that can be used by the platform code to expose that information for the benefit of the other parts of the kernel and make the ACPI core set them as appropriate. Users of the new flags will be added later. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 3 +++ include/linux/suspend.h | 36 ++++++++++++++++++++++++++++++++++++ kernel/power/suspend.c | 4 ++++ 3 files changed, 43 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2f0d4db40a9e..a0b4d781e606 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -487,6 +487,8 @@ static int acpi_suspend_begin(suspend_state_t pm_state) pr_err("ACPI does not support sleep state S%u\n", acpi_state); return -ENOSYS; } + if (acpi_state > ACPI_STATE_S1) + pm_set_suspend_via_firmware(); acpi_pm_start(acpi_state); return 0; @@ -522,6 +524,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) if (error) return error; pr_info(PREFIX "Low-level resume complete\n"); + pm_set_resume_via_firmware(); break; } trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 33aaf9a9596c..8b6ec7ef0854 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -202,6 +202,36 @@ struct platform_freeze_ops { extern void suspend_set_ops(const struct platform_suspend_ops *ops); extern int suspend_valid_only_mem(suspend_state_t state); +extern unsigned int pm_suspend_global_flags; + +#define PM_SUSPEND_FLAG_FW_SUSPEND (1 << 0) +#define PM_SUSPEND_FLAG_FW_RESUME (1 << 1) + +static inline void pm_suspend_clear_flags(void) +{ + pm_suspend_global_flags = 0; +} + +static inline void pm_set_suspend_via_firmware(void) +{ + pm_suspend_global_flags |= PM_SUSPEND_FLAG_FW_SUSPEND; +} + +static inline void pm_set_resume_via_firmware(void) +{ + pm_suspend_global_flags |= PM_SUSPEND_FLAG_FW_RESUME; +} + +static inline bool pm_suspend_via_firmware(void) +{ + return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_SUSPEND); +} + +static inline bool pm_resume_via_firmware(void) +{ + return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_RESUME); +} + /* Suspend-to-idle state machnine. */ enum freeze_state { FREEZE_STATE_NONE, /* Not suspended/suspending. */ @@ -241,6 +271,12 @@ extern int pm_suspend(suspend_state_t state); #else /* !CONFIG_SUSPEND */ #define suspend_valid_only_mem NULL +static inline void pm_suspend_clear_flags(void) {} +static inline void pm_set_suspend_via_firmware(void) {} +static inline void pm_set_resume_via_firmware(void) {} +static inline bool pm_suspend_via_firmware(void) { return false; } +static inline bool pm_resume_via_firmware(void) { return false; } + static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } static inline bool idle_should_freeze(void) { return false; } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 7e4cda4a8dd9..f9fe133c13e2 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -35,6 +35,9 @@ const char *pm_labels[] = { "mem", "standby", "freeze", NULL }; const char *pm_states[PM_SUSPEND_MAX]; +unsigned int pm_suspend_global_flags; +EXPORT_SYMBOL_GPL(pm_suspend_global_flags); + static const struct platform_suspend_ops *suspend_ops; static const struct platform_freeze_ops *freeze_ops; static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); @@ -493,6 +496,7 @@ static int enter_state(suspend_state_t state) #endif pr_debug("PM: Preparing system for sleep (%s)\n", pm_states[state]); + pm_suspend_clear_flags(); error = suspend_prepare(state); if (error) goto Unlock; -- cgit v1.2.3 From 58a1fbbb2ee873dd1fe327e80bc7b08e80866269 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Oct 2015 00:50:24 +0200 Subject: PM / PCI / ACPI: Kick devices that might have been reset by firmware There is a concern that if the platform firmware was involved in the system resume that's being completed, some devices might have been reset by it and if those devices had the power.direct_complete flag set during the preceding suspend transition, they may stay in a reset-power-on state indefinitely (until they are runtime-resumed and then suspended again). That may not be a big deal from the individual device's perspective, but if the system is an SoC, it may be prevented from entering deep SoC-wide low-power states on idle because of that. The devices that are most likely to be affected by this issue are PCI devices and ACPI-enumerated devices using the general ACPI PM domain, so to prevent it from happening for those devices, force a runtime resume for them if they have their power.direct_complete flags set and the platform firmware was involved in the resume transition currently in progress. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_lpss.c | 2 +- drivers/acpi/device_pm.c | 19 +------------------ drivers/base/power/generic_ops.c | 23 +++++++++++++++++++++++ drivers/pci/pci-driver.c | 2 +- include/linux/pm.h | 1 + 5 files changed, 27 insertions(+), 20 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index f51bd0d0bc17..f9e0d09f7c66 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -664,7 +664,7 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_lpss_suspend_late, .resume_early = acpi_lpss_resume_early, diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4806b7f856c4..08a02cdc737c 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -962,23 +962,6 @@ int acpi_subsys_prepare(struct device *dev) } EXPORT_SYMBOL_GPL(acpi_subsys_prepare); -/** - * acpi_subsys_complete - Finalize device's resume during system resume. - * @dev: Device to handle. - */ -void acpi_subsys_complete(struct device *dev) -{ - pm_generic_complete(dev); - /* - * If the device had been runtime-suspended before the system went into - * the sleep state it is going out of and it has never been resumed till - * now, resume it in case the firmware powered it up. - */ - if (dev->power.direct_complete) - pm_request_resume(dev); -} -EXPORT_SYMBOL_GPL(acpi_subsys_complete); - /** * acpi_subsys_suspend - Run the device driver's suspend callback. * @dev: Device to handle. @@ -1047,7 +1030,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { .runtime_resume = acpi_subsys_runtime_resume, #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_subsys_suspend_late, .resume_early = acpi_subsys_resume_early, diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index b2ed606265a8..07c3c4a9522d 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef CONFIG_PM /** @@ -297,4 +298,26 @@ void pm_generic_complete(struct device *dev) if (drv && drv->pm && drv->pm->complete) drv->pm->complete(dev); } + +/** + * pm_complete_with_resume_check - Complete a device power transition. + * @dev: Device to handle. + * + * Complete a device power transition during a system-wide power transition and + * optionally schedule a runtime resume of the device if the system resume in + * progress has been initated by the platform firmware and the device had its + * power.direct_complete flag set. + */ +void pm_complete_with_resume_check(struct device *dev) +{ + pm_generic_complete(dev); + /* + * If the device had been runtime-suspended before the system went into + * the sleep state it is going out of and it has never been resumed till + * now, resume it in case the firmware powered it up. + */ + if (dev->power.direct_complete && pm_resume_via_firmware()) + pm_request_resume(dev); +} +EXPORT_SYMBOL_GPL(pm_complete_with_resume_check); #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c9ce3073d7fe..306124bba61e 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -687,7 +687,7 @@ static int pci_pm_prepare(struct device *dev) static void pci_pm_complete(struct device *dev) { pci_dev_complete_resume(to_pci_dev(dev)); - pm_generic_complete(dev); + pm_complete_with_resume_check(dev); } #else /* !CONFIG_PM_SLEEP */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 35d599e7250d..528be6787796 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -732,6 +732,7 @@ extern int pm_generic_poweroff_noirq(struct device *dev); extern int pm_generic_poweroff_late(struct device *dev); extern int pm_generic_poweroff(struct device *dev); extern void pm_generic_complete(struct device *dev); +extern void pm_complete_with_resume_check(struct device *dev); #else /* !CONFIG_PM_SLEEP */ -- cgit v1.2.3 From 9b3fedde27d3d63055c43c05e8254e252e58ba48 Mon Sep 17 00:00:00 2001 From: Lukasz Anaczkowski Date: Wed, 9 Sep 2015 15:47:28 +0200 Subject: ACPI / tables: Add acpi_subtable_proc to ACPI table parsers ACPI subtable parsing needs to be extended to allow two or more handlers to be run in the same ACPI table walk, thus adding acpi_subtable_proc structure which stores () ACPI table id () handler that processes table () counter how many items has been processed and passing it to acpi_parse_entries_array() and acpi_table_parse_entries_array(). This is needed to fix CPU enumeration when APIC/X2APIC entries are interleaved. Signed-off-by: Lukasz Anaczkowski Reviewed-by: Marc Zyngier Tested-by: Marc Zyngier Signed-off-by: Rafael J. Wysocki --- drivers/acpi/tables.c | 93 +++++++++++++++++++++++++++++++++++++++++---------- include/linux/acpi.h | 19 +++++++++-- 2 files changed, 91 insertions(+), 21 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 17a6fa01a338..c1ff58de99e9 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -210,20 +210,39 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) } } -int __init -acpi_parse_entries(char *id, unsigned long table_size, - acpi_tbl_entry_handler handler, +/** + * acpi_parse_entries_array - for each proc_num find a suitable subtable + * + * @id: table id (for debugging purposes) + * @table_size: single entry size + * @table_header: where does the table start? + * @proc: array of acpi_subtable_proc struct containing entry id + * and associated handler with it + * @proc_num: how big proc is? + * @max_entries: how many entries can we process? + * + * For each proc_num find a subtable with proc->id and run proc->handler + * on it. Assumption is that there's only single handler for particular + * entry id. + * + * On success returns sum of all matching entries for all proc handlers. + * Otherwise, -ENODEV or -EINVAL is returned. + */ +static int __init +acpi_parse_entries_array(char *id, unsigned long table_size, struct acpi_table_header *table_header, - int entry_id, unsigned int max_entries) + struct acpi_subtable_proc *proc, int proc_num, + unsigned int max_entries) { struct acpi_subtable_header *entry; - int count = 0; unsigned long table_end; + int count = 0; + int i; if (acpi_disabled) return -ENODEV; - if (!id || !handler) + if (!id) return -EINVAL; if (!table_size) @@ -243,20 +262,27 @@ acpi_parse_entries(char *id, unsigned long table_size, while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < table_end) { - if (entry->type == entry_id - && (!max_entries || count < max_entries)) { - if (handler(entry, table_end)) + if (max_entries && count >= max_entries) + break; + + for (i = 0; i < proc_num; i++) { + if (entry->type != proc[i].id) + continue; + if (!proc->handler || proc[i].handler(entry, table_end)) return -EINVAL; - count++; + proc->count++; + break; } + if (i != proc_num) + count++; /* * If entry->length is 0, break from this loop to avoid * infinite loop. */ if (entry->length == 0) { - pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id); + pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); return -EINVAL; } @@ -266,17 +292,32 @@ acpi_parse_entries(char *id, unsigned long table_size, if (max_entries && count > max_entries) { pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", - id, entry_id, count - max_entries, count); + id, proc->id, count - max_entries, count); } return count; } int __init -acpi_table_parse_entries(char *id, +acpi_parse_entries(char *id, + unsigned long table_size, + acpi_tbl_entry_handler handler, + struct acpi_table_header *table_header, + int entry_id, unsigned int max_entries) +{ + struct acpi_subtable_proc proc = { + .id = entry_id, + .handler = handler, + }; + + return acpi_parse_entries_array(id, table_size, table_header, + &proc, 1, max_entries); +} + +int __init +acpi_table_parse_entries_array(char *id, unsigned long table_size, - int entry_id, - acpi_tbl_entry_handler handler, + struct acpi_subtable_proc *proc, int proc_num, unsigned int max_entries) { struct acpi_table_header *table_header = NULL; @@ -287,7 +328,7 @@ acpi_table_parse_entries(char *id, if (acpi_disabled) return -ENODEV; - if (!id || !handler) + if (!id) return -EINVAL; if (!strncmp(id, ACPI_SIG_MADT, 4)) @@ -299,13 +340,29 @@ acpi_table_parse_entries(char *id, return -ENODEV; } - count = acpi_parse_entries(id, table_size, handler, table_header, - entry_id, max_entries); + count = acpi_parse_entries_array(id, table_size, table_header, + proc, proc_num, max_entries); early_acpi_os_unmap_memory((char *)table_header, tbl_size); return count; } +int __init +acpi_table_parse_entries(char *id, + unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries) +{ + struct acpi_subtable_proc proc = { + .id = entry_id, + .handler = handler, + }; + + return acpi_table_parse_entries_array(id, table_size, &proc, 1, + max_entries); +} + int __init acpi_table_parse_madt(enum acpi_madt_type id, acpi_tbl_entry_handler handler, unsigned int max_entries) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7235c4851460..b0299f8db660 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -131,6 +131,12 @@ static inline void acpi_initrd_override(void *data, size_t size) (!entry) || (unsigned long)entry + sizeof(*entry) > end || \ ((struct acpi_subtable_header *)entry)->length < sizeof(*entry)) +struct acpi_subtable_proc { + int id; + acpi_tbl_entry_handler handler; + int count; +}; + char * __acpi_map_table (unsigned long phys_addr, unsigned long size); void __acpi_unmap_table(char *map, unsigned long size); int early_acpi_boot_init(void); @@ -146,9 +152,16 @@ int __init acpi_parse_entries(char *id, unsigned long table_size, struct acpi_table_header *table_header, int entry_id, unsigned int max_entries); int __init acpi_table_parse_entries(char *id, unsigned long table_size, - int entry_id, - acpi_tbl_entry_handler handler, - unsigned int max_entries); + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries); +int __init acpi_table_parse_entries(char *id, unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries); +int __init acpi_table_parse_entries_array(char *id, unsigned long table_size, + struct acpi_subtable_proc *proc, int proc_num, + unsigned int max_entries); int acpi_table_parse_madt(enum acpi_madt_type id, acpi_tbl_entry_handler handler, unsigned int max_entries); -- cgit v1.2.3 From 362414d9d2824ee0f4910a73e880860a90e75c3c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 22 Sep 2015 15:29:25 +0300 Subject: ACPI / tables: test the correct variable The intent was to test "proc[i].handler" instead of "proc->handler". Signed-off-by: Dan Carpenter Signed-off-by: Rafael J. Wysocki --- drivers/acpi/tables.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index c1ff58de99e9..6c0f0794aa82 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -268,7 +268,8 @@ acpi_parse_entries_array(char *id, unsigned long table_size, for (i = 0; i < proc_num; i++) { if (entry->type != proc[i].id) continue; - if (!proc->handler || proc[i].handler(entry, table_end)) + if (!proc[i].handler || + proc[i].handler(entry, table_end)) return -EINVAL; proc->count++; -- cgit v1.2.3 From 91236ecc74a25431138f71b6d52e130cd0f774b3 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:36 +0800 Subject: ACPI/PCI: Enhance ACPI core to support sparse IO space Enhance ACPI resource parsing interfaces to support sparse IO space, which will be used to share common code between x86 and IA64 later. Tested-by: Tony Luck Signed-off-by: Jiang Liu Reviewed-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki --- drivers/acpi/resource.c | 9 ++++++--- include/linux/ioport.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 15d22db05054..cdc5c2599beb 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -119,7 +119,7 @@ bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); static void acpi_dev_ioresource_flags(struct resource *res, u64 len, - u8 io_decode) + u8 io_decode, u8 translation_type) { res->flags = IORESOURCE_IO; @@ -131,6 +131,8 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (io_decode == ACPI_DECODE_16) res->flags |= IORESOURCE_IO_16BIT_ADDR; + if (translation_type == ACPI_SPARSE_TRANSLATION) + res->flags |= IORESOURCE_IO_SPARSE; } static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, @@ -138,7 +140,7 @@ static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, { res->start = start; res->end = start + len - 1; - acpi_dev_ioresource_flags(res, len, io_decode); + acpi_dev_ioresource_flags(res, len, io_decode, 0); } /** @@ -231,7 +233,8 @@ static bool acpi_decode_space(struct resource_win *win, acpi_dev_memresource_flags(res, len, wp); break; case ACPI_IO_RANGE: - acpi_dev_ioresource_flags(res, len, iodec); + acpi_dev_ioresource_flags(res, len, iodec, + addr->info.io.translation_type); break; case ACPI_BUS_NUMBER_RANGE: res->flags = IORESOURCE_BUS; diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 388e3ae94f7a..24bea087e7af 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -94,6 +94,7 @@ struct resource { /* PnP I/O specific bits (IORESOURCE_BITS) */ #define IORESOURCE_IO_16BIT_ADDR (1<<0) #define IORESOURCE_IO_FIXED (1<<1) +#define IORESOURCE_IO_SPARSE (1<<2) /* PCI ROM control bits (IORESOURCE_BITS) */ #define IORESOURCE_ROM_ENABLE (1<<0) /* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */ -- cgit v1.2.3 From 2c204383a2922cd6b79b9d78680a049a2144fbcc Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:39 +0800 Subject: PCI/ACPI: Add interface acpi_pci_root_create() Introduce common interface acpi_pci_root_create() and related data structures to create PCI root bus for ACPI PCI host bridges. It will be used to kill duplicated arch specific code for IA64 and x86. It may also help ARM64 in future. Reviewed-by: Lorenzo Pieralisi Tested-by: Tony Luck Signed-off-by: Jiang Liu Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 24 ++++++ 2 files changed, 228 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 393706a5261b..850d7bf0c873 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -652,6 +652,210 @@ static void acpi_pci_root_remove(struct acpi_device *device) kfree(root); } +/* + * Following code to support acpi_pci_root_create() is copied from + * arch/x86/pci/acpi.c and modified so it could be reused by x86, IA64 + * and ARM64. + */ +static void acpi_pci_root_validate_resources(struct device *dev, + struct list_head *resources, + unsigned long type) +{ + LIST_HEAD(list); + struct resource *res1, *res2, *root = NULL; + struct resource_entry *tmp, *entry, *entry2; + + BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); + root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; + + list_splice_init(resources, &list); + resource_list_for_each_entry_safe(entry, tmp, &list) { + bool free = false; + resource_size_t end; + + res1 = entry->res; + if (!(res1->flags & type)) + goto next; + + /* Exclude non-addressable range or non-addressable portion */ + end = min(res1->end, root->end); + if (end <= res1->start) { + dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", + res1); + free = true; + goto next; + } else if (res1->end != end) { + dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", + res1, (unsigned long long)end + 1, + (unsigned long long)res1->end); + res1->end = end; + } + + resource_list_for_each_entry(entry2, resources) { + res2 = entry2->res; + if (!(res2->flags & type)) + continue; + + /* + * I don't like throwing away windows because then + * our resources no longer match the ACPI _CRS, but + * the kernel resource tree doesn't allow overlaps. + */ + if (resource_overlaps(res1, res2)) { + res2->start = min(res1->start, res2->start); + res2->end = max(res1->end, res2->end); + dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", + res2, res1); + free = true; + goto next; + } + } + +next: + resource_list_del(entry); + if (free) + resource_list_free_entry(entry); + else + resource_list_add_tail(entry, resources); + } +} + +int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) +{ + int ret; + struct list_head *list = &info->resources; + struct acpi_device *device = info->bridge; + struct resource_entry *entry, *tmp; + unsigned long flags; + + flags = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_MEM_8AND16BIT; + ret = acpi_dev_get_resources(device, list, + acpi_dev_filter_resource_type_cb, + (void *)flags); + if (ret < 0) + dev_warn(&device->dev, + "failed to parse _CRS method, error code %d\n", ret); + else if (ret == 0) + dev_dbg(&device->dev, + "no IO and memory resources present in _CRS\n"); + else { + resource_list_for_each_entry_safe(entry, tmp, list) { + if (entry->res->flags & IORESOURCE_DISABLED) + resource_list_destroy_entry(entry); + else + entry->res->name = info->name; + } + acpi_pci_root_validate_resources(&device->dev, list, + IORESOURCE_MEM); + acpi_pci_root_validate_resources(&device->dev, list, + IORESOURCE_IO); + } + + return ret; +} + +static void pci_acpi_root_add_resources(struct acpi_pci_root_info *info) +{ + struct resource_entry *entry, *tmp; + struct resource *res, *conflict, *root = NULL; + + resource_list_for_each_entry_safe(entry, tmp, &info->resources) { + res = entry->res; + if (res->flags & IORESOURCE_MEM) + root = &iomem_resource; + else if (res->flags & IORESOURCE_IO) + root = &ioport_resource; + else + continue; + + conflict = insert_resource_conflict(root, res); + if (conflict) { + dev_info(&info->bridge->dev, + "ignoring host bridge window %pR (conflicts with %s %pR)\n", + res, conflict->name, conflict); + resource_list_destroy_entry(entry); + } + } +} + +static void __acpi_pci_root_release_info(struct acpi_pci_root_info *info) +{ + struct resource *res; + struct resource_entry *entry, *tmp; + + if (!info) + return; + + resource_list_for_each_entry_safe(entry, tmp, &info->resources) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + resource_list_destroy_entry(entry); + } + + info->ops->release_info(info); +} + +static void acpi_pci_root_release_info(struct pci_host_bridge *bridge) +{ + struct resource *res; + struct resource_entry *entry; + + resource_list_for_each_entry(entry, &bridge->windows) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + } + __acpi_pci_root_release_info(bridge->release_data); +} + +struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, + struct acpi_pci_root_ops *ops, + struct acpi_pci_root_info *info, + void *sysdata) +{ + int ret, busnum = root->secondary.start; + struct acpi_device *device = root->device; + int node = acpi_get_node(device->handle); + struct pci_bus *bus; + + info->root = root; + info->bridge = device; + info->ops = ops; + INIT_LIST_HEAD(&info->resources); + snprintf(info->name, sizeof(info->name), "PCI Bus %04x:%02x", + root->segment, busnum); + + if (ops->init_info && ops->init_info(info)) + goto out_release_info; + if (ops->prepare_resources) + ret = ops->prepare_resources(info); + else + ret = acpi_pci_probe_root_resources(info); + if (ret < 0) + goto out_release_info; + + pci_acpi_root_add_resources(info); + pci_add_resource(&info->resources, &root->secondary); + bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, + sysdata, &info->resources); + if (!bus) + goto out_release_info; + + pci_scan_child_bus(bus); + pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), + acpi_pci_root_release_info, info); + if (node != NUMA_NO_NODE) + dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); + return bus; + +out_release_info: + __acpi_pci_root_release_info(info); + return NULL; +} + void __init acpi_pci_root_init(void) { acpi_hest_init(); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index a965efa52152..89ab0572dbc6 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -52,6 +52,30 @@ static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus) return ACPI_HANDLE(dev); } +struct acpi_pci_root; +struct acpi_pci_root_ops; + +struct acpi_pci_root_info { + struct acpi_pci_root *root; + struct acpi_device *bridge; + struct acpi_pci_root_ops *ops; + struct list_head resources; + char name[16]; +}; + +struct acpi_pci_root_ops { + struct pci_ops *pci_ops; + int (*init_info)(struct acpi_pci_root_info *info); + void (*release_info)(struct acpi_pci_root_info *info); + int (*prepare_resources)(struct acpi_pci_root_info *info); +}; + +extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info); +extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, + struct acpi_pci_root_ops *ops, + struct acpi_pci_root_info *info, + void *sd); + void acpi_pci_add_bus(struct pci_bus *bus); void acpi_pci_remove_bus(struct pci_bus *bus); -- cgit v1.2.3 From 205ad97fc5a6386214323641dd28b822cb6fc624 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 Oct 2015 18:32:27 +0200 Subject: ACPI / property: Fix subnode lookup scope for data-only subnodes The correct scope for looking up the objects to generate data packages for data-only subnodes pointed to by another data-only subnode is the scope of the parent of that subnode and not the scope containing the _DSD object at the top of the hierarchy (the latter works only if all of the objects returning data-only subnode packages in a given hierarchy are in the same scope). Fix the code to work as expected. Fixes: 445b0eb058f5 (ACPI / property: Add support for data-only subnodes) Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg --- drivers/acpi/property.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 14654435c295..88f4306744c0 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -71,7 +71,14 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, if (acpi_extract_properties(buf.pointer, &dn->data)) dn->handle = handle; - if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) + /* + * The scope for the subnode object lookup is the one of the namespace + * node (device) containing the object that has returned the package. + * That is, it's the scope of that object's parent. + */ + status = acpi_get_parent(handle, &scope); + if (ACPI_SUCCESS(status) + && acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) dn->handle = handle; if (dn->handle) { -- cgit v1.2.3 From 93556118c0679d9ed25e108a94f2b893160fce54 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:26 +0800 Subject: ACPICA: Remove unnecessary conditional compilation ACPICA commit eea1f0e561893b6d6417913b2d224082fe3a0a5e Remove use of ACPI_DEBUGGER and ACPI_DISASSEMBLER where these defines are used around entire modules. Note: This type of code also causes problems with IDEs. Link: https://github.com/acpica/acpica/commit/eea1f0e5 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/Makefile | 2 +- drivers/acpi/acpica/acdebug.h | 6 ++++++ drivers/acpi/acpica/rsdump.c | 3 --- include/acpi/platform/acenv.h | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index fedcc16b56cc..ac78d7657204 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -123,7 +123,6 @@ acpi-y += \ rsaddr.o \ rscalc.o \ rscreate.o \ - rsdump.o \ rsdumpinfo.o \ rsinfo.o \ rsio.o \ @@ -179,6 +178,7 @@ acpi-y += \ utxfmutex.o acpi-$(ACPI_FUTURE_USAGE) += \ + rsdump.o \ utcache.o \ utfileio.o \ utprint.o \ diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index eb2e926d8218..c928ba494c40 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -44,6 +44,12 @@ #ifndef __ACDEBUG_H__ #define __ACDEBUG_H__ +/* The debugger is used in conjunction with the disassembler most of time */ + +#ifdef ACPI_DISASSEMBLER +#include "acdisasm.h" +#endif + #define ACPI_DEBUG_BUFFER_SIZE 0x4000 /* 16K buffer for return objects */ struct acpi_db_command_info { diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index c428bb33204e..2a09288e7c57 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -51,7 +51,6 @@ ACPI_MODULE_NAME("rsdump") /* * All functions in this module are used by the AML Debugger only */ -#if defined(ACPI_DEBUGGER) /* Local prototypes */ static void acpi_rs_out_string(char *title, char *value); @@ -565,5 +564,3 @@ static void acpi_rs_dump_word_list(u16 length, u16 *data) acpi_os_printf("%25s%2.2X : %4.4X\n", "Word", i, data[i]); } } - -#endif diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index ec00e2bb029e..15ef08c04870 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -142,7 +142,7 @@ #ifdef ACPI_LIBRARY #define ACPI_USE_LOCAL_CACHE -#define ACPI_FUTURE_USAGE +#define ACPI_FULL_DEBUG #endif /* Common for all ACPICA applications */ -- cgit v1.2.3 From 69058ddf28ce86296b1526d0953417f29b6df179 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:32 +0800 Subject: ACPICA: iASL: Add symbolic operator support for Index() operator ACPICA commit fbe67c46830f10c839941f8512cac5bddcb86bd3 Index (XXXX, 2) is now supported by XXXX [2] This patch doesn't affect Linux kernel. Link: https://github.com/acpica/acpica/commit/fbe67c46 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/aclocal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 6f708267ad8c..8a66fefecb87 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -848,7 +848,7 @@ struct acpi_parse_state { #define ACPI_PARSEOP_PARAMLIST 0x02 #define ACPI_PARSEOP_EMPTY_TERMLIST 0x04 #define ACPI_PARSEOP_PREDEF_CHECKED 0x08 -#define ACPI_PARSEOP_SPECIAL 0x10 +#define ACPI_PARSEOP_CLOSING_PAREN 0x10 #define ACPI_PARSEOP_COMPOUND 0x20 #define ACPI_PARSEOP_ASSIGNMENT 0x40 -- cgit v1.2.3 From 92dc96196e96470eb9be7bc3f61f12f1571a2d30 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:38 +0800 Subject: ACPICA: Update exception code for "file not found" error ACPICA commit ac1564c26d239348ef13455f61d5616f3961ff43 Used by the ACPICA applications. This patch is a bit broken due to non-portable inclusion as on some platforms, there is no such a header file for their lib-c exports. Fortunately, Linux doesn't compile utfileio.c for either the kernel space ACPICA core (drivers/acpi/acpica) or the userspace ACPICA tools (tools/power/acpi) for now, so it's safe to leave this patch as it is. Link: https://github.com/acpica/acpica/commit/ac1564c2 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utfileio.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c index 75a94f52b4be..d435b7b7eb94 100644 --- a/drivers/acpi/acpica/utfileio.c +++ b/drivers/acpi/acpica/utfileio.c @@ -45,6 +45,7 @@ #include "accommon.h" #include "actables.h" #include "acapps.h" +#include "errno.h" #ifdef ACPI_ASL_COMPILER #include "aslcompiler.h" @@ -301,6 +302,11 @@ acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table) file = fopen(filename, "rb"); if (!file) { perror("Could not open input file"); + + if (errno == ENOENT) { + return (AE_NOT_EXIST); + } + return (status); } -- cgit v1.2.3 From cd64bbf8ce7b27d4a88d533883b59f79c6bbc9f8 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:45 +0800 Subject: ACPICA: Debugger: Update mutexes used for multithreaded debugger ACPICA commit 6b2701f619040e803313363f516b200e362a9100 Make these mutex objects independent of the deadlock detection mechanism. This mechanism caused failures with the multithread debugger. This patch doesn't affect Linux kernel as debugger is currently not fully functioning in the Linux kernel. And the further debugger cleanups will take care of handling debugger command signalling correctly instead of using such kind of mutexes. So it is safe to leave this patch as it is. Link: https://github.com/acpica/acpica/commit/6b2701f6 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acglobal.h | 3 +++ drivers/acpi/acpica/aclocal.h | 4 +--- drivers/acpi/acpica/utdecode.c | 2 -- drivers/acpi/acpica/utmutex.c | 21 +++++++++++++++++++++ include/acpi/platform/acenv.h | 6 +++--- 5 files changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 4dde37c3d8fc..71a3c40dbf20 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -358,6 +358,9 @@ ACPI_GLOBAL(u16, acpi_gbl_node_type_count_misc); ACPI_GLOBAL(u32, acpi_gbl_num_nodes); ACPI_GLOBAL(u32, acpi_gbl_num_objects); +ACPI_GLOBAL(acpi_mutex, acpi_gbl_db_command_ready); +ACPI_GLOBAL(acpi_mutex, acpi_gbl_db_command_complete); + #endif /* ACPI_DEBUGGER */ /***************************************************************************** diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 8a66fefecb87..918f70d15fda 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -83,10 +83,8 @@ union acpi_parse_object; #define ACPI_MTX_EVENTS 3 /* Data for ACPI events */ #define ACPI_MTX_CACHES 4 /* Internal caches, general purposes */ #define ACPI_MTX_MEMORY 5 /* Debug memory tracking lists */ -#define ACPI_MTX_DEBUG_CMD_COMPLETE 6 /* AML debugger */ -#define ACPI_MTX_DEBUG_CMD_READY 7 /* AML debugger */ -#define ACPI_MAX_MUTEX 7 +#define ACPI_MAX_MUTEX 5 #define ACPI_NUM_MUTEX ACPI_MAX_MUTEX+1 /* Lock structure for reader/writer interfaces */ diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index 988e23b7795c..d452a7837c8c 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -407,8 +407,6 @@ static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = { "ACPI_MTX_Events", "ACPI_MTX_Caches", "ACPI_MTX_Memory", - "ACPI_MTX_CommandComplete", - "ACPI_MTX_CommandReady" }; char *acpi_ut_get_mutex_name(u32 mutex_id) diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 37b8b58fcd56..ce406e39b669 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -108,6 +108,21 @@ acpi_status acpi_ut_mutex_initialize(void) /* Create the reader/writer lock for namespace access */ status = acpi_ut_create_rw_lock(&acpi_gbl_namespace_rw_lock); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } +#ifdef ACPI_DEBUGGER + + /* Debugger Support */ + + status = acpi_os_create_mutex(&acpi_gbl_db_command_ready); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_os_create_mutex(&acpi_gbl_db_command_complete); +#endif + return_ACPI_STATUS(status); } @@ -147,6 +162,12 @@ void acpi_ut_mutex_terminate(void) /* Delete the reader/writer lock */ acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_lock); + +#ifdef ACPI_DEBUGGER + acpi_os_delete_mutex(acpi_gbl_db_command_ready); + acpi_os_delete_mutex(acpi_gbl_db_command_complete); +#endif + return_VOID; } diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 15ef08c04870..056f245ad038 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -304,11 +304,11 @@ * multi-threaded if ACPI_APPLICATION is not set. */ #ifndef DEBUGGER_THREADING -#ifdef ACPI_APPLICATION -#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED +#if !defined (ACPI_APPLICATION) || defined (ACPI_EXEC_APP) +#define DEBUGGER_THREADING DEBUGGER_MULTI_THREADED #else -#define DEBUGGER_THREADING DEBUGGER_MULTI_THREADED +#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED #endif #endif /* !DEBUGGER_THREADING */ -- cgit v1.2.3 From ca321d1ca6723ed0e04edd09de49c92b24e3648e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:52 +0800 Subject: ACPICA: Update NFIT table to rename a flags field ACPICA commit 534deab97fb416a13bfede15c538e2c9eac9384a Updated one of the memory subtable flags to clarify. Link: https://github.com/acpica/acpica/commit/534deab9 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/nfit.c | 6 +++--- drivers/acpi/nfit.h | 2 +- include/acpi/actbl1.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index c1b8d03e262e..bc18aa213bb1 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -706,7 +706,7 @@ static ssize_t flags_show(struct device *dev, flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "", flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore_fail " : "", flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush_fail " : "", - flags & ACPI_NFIT_MEM_ARMED ? "not_armed " : "", + flags & ACPI_NFIT_MEM_NOT_ARMED ? "not_armed " : "", flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart_event " : ""); } static DEVICE_ATTR_RO(flags); @@ -815,7 +815,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) flags |= NDD_ALIASING; mem_flags = __to_nfit_memdev(nfit_mem)->flags; - if (mem_flags & ACPI_NFIT_MEM_ARMED) + if (mem_flags & ACPI_NFIT_MEM_NOT_ARMED) flags |= NDD_UNARMED; rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle); @@ -839,7 +839,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "", mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"", mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? " flush_fail" : "", - mem_flags & ACPI_NFIT_MEM_ARMED ? " not_armed" : ""); + mem_flags & ACPI_NFIT_MEM_NOT_ARMED ? " not_armed" : ""); } diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 7e740156b9c2..329a1eba0c16 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -24,7 +24,7 @@ #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" #define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ - | ACPI_NFIT_MEM_ARMED) + | ACPI_NFIT_MEM_NOT_ARMED) enum nfit_uuids { NFIT_SPA_VOLATILE, diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index fcd570999f35..1bb979e3e3f5 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -1012,7 +1012,7 @@ struct acpi_nfit_memory_map { #define ACPI_NFIT_MEM_SAVE_FAILED (1) /* 00: Last SAVE to Memory Device failed */ #define ACPI_NFIT_MEM_RESTORE_FAILED (1<<1) /* 01: Last RESTORE from Memory Device failed */ #define ACPI_NFIT_MEM_FLUSH_FAILED (1<<2) /* 02: Platform flush failed */ -#define ACPI_NFIT_MEM_ARMED (1<<3) /* 03: Memory Device observed to be not armed */ +#define ACPI_NFIT_MEM_NOT_ARMED (1<<3) /* 03: Memory Device is not armed */ #define ACPI_NFIT_MEM_HEALTH_OBSERVED (1<<4) /* 04: Memory Device observed SMART/health events */ #define ACPI_NFIT_MEM_HEALTH_ENABLED (1<<5) /* 05: SMART/health events enabled */ -- cgit v1.2.3 From a5922a1f738b077c9f341f4bf3c02be436d39687 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:58 +0800 Subject: ACPICA: Improve typechecking, both compile-time and runtime ACPICA commit 8d0f96e2a11a4ceabb2cae4b41e0ce1f4d3786b9 Adds much stricter typechecking in the iASL compiler, and also adds some additional checking in the interpreter. Link: https://github.com/acpica/acpica/commit/8d0f96e2 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/aclocal.h | 8 ++- drivers/acpi/acpica/acopcode.h | 4 +- drivers/acpi/acpica/amlcode.h | 11 ++-- drivers/acpi/acpica/exconvrt.c | 1 + drivers/acpi/acpica/exresolv.c | 1 - drivers/acpi/acpica/exresop.c | 2 + drivers/acpi/acpica/exstore.c | 120 +++++++++++++++++++++++++++++++---------- drivers/acpi/acpica/exstoren.c | 5 +- drivers/acpi/acpica/nspredef.c | 2 +- drivers/acpi/acpica/utdecode.c | 19 ++++++- include/acpi/acexcep.h | 7 ++- 11 files changed, 134 insertions(+), 46 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 918f70d15fda..4e41b43ebad3 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -285,13 +285,17 @@ acpi_status(*acpi_internal_method) (struct acpi_walk_state * walk_state); #define ACPI_BTYPE_BUFFER_FIELD 0x00002000 #define ACPI_BTYPE_DDB_HANDLE 0x00004000 #define ACPI_BTYPE_DEBUG_OBJECT 0x00008000 -#define ACPI_BTYPE_REFERENCE 0x00010000 +#define ACPI_BTYPE_REFERENCE_OBJECT 0x00010000 /* From Index(), ref_of(), etc (type6_opcodes) */ #define ACPI_BTYPE_RESOURCE 0x00020000 +#define ACPI_BTYPE_NAMED_REFERENCE 0x00040000 /* Generic unresolved Name or Namepath */ #define ACPI_BTYPE_COMPUTE_DATA (ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_BUFFER) #define ACPI_BTYPE_DATA (ACPI_BTYPE_COMPUTE_DATA | ACPI_BTYPE_PACKAGE) -#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE | ACPI_BTYPE_DDB_HANDLE) + + /* Used by Copy, de_ref_of, Store, Printf, Fprintf */ + +#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE_OBJECT | ACPI_BTYPE_DDB_HANDLE) #define ACPI_BTYPE_DEVICE_OBJECTS (ACPI_BTYPE_DEVICE | ACPI_BTYPE_THERMAL | ACPI_BTYPE_PROCESSOR) #define ACPI_BTYPE_OBJECTS_AND_REFS 0x0001FFFF /* ARG or LOCAL */ #define ACPI_BTYPE_ALL_OBJECTS 0x0000FFFF diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h index fd85ad05a24a..f9acf92fa0bc 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -211,7 +211,7 @@ #define ARGI_ARG4 ARG_NONE #define ARGI_ARG5 ARG_NONE #define ARGI_ARG6 ARG_NONE -#define ARGI_BANK_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_BANK_FIELD_OP ARGI_LIST1 (ARGI_INTEGER) #define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) @@ -307,7 +307,7 @@ #define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_INTEGER) #define ARGI_STALL_OP ARGI_LIST1 (ARGI_INTEGER) #define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE -#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_TARGETREF) +#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_STORE_TARGET) #define ARGI_STRING_OP ARGI_INVALID_OPCODE #define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index be9fd009cb28..883f20cfa698 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -277,14 +277,15 @@ #define ARGI_TARGETREF 0x0F /* Target, subject to implicit conversion */ #define ARGI_FIXED_TARGET 0x10 /* Target, no implicit conversion */ #define ARGI_SIMPLE_TARGET 0x11 /* Name, Local, Arg -- no implicit conversion */ +#define ARGI_STORE_TARGET 0x12 /* Target for store is TARGETREF + package objects */ /* Multiple/complex types */ -#define ARGI_DATAOBJECT 0x12 /* Buffer, String, package or reference to a node - Used only by size_of operator */ -#define ARGI_COMPLEXOBJ 0x13 /* Buffer, String, or package (Used by INDEX op only) */ -#define ARGI_REF_OR_STRING 0x14 /* Reference or String (Used by DEREFOF op only) */ -#define ARGI_REGION_OR_BUFFER 0x15 /* Used by LOAD op only */ -#define ARGI_DATAREFOBJ 0x16 +#define ARGI_DATAOBJECT 0x13 /* Buffer, String, package or reference to a node - Used only by size_of operator */ +#define ARGI_COMPLEXOBJ 0x14 /* Buffer, String, or package (Used by INDEX op only) */ +#define ARGI_REF_OR_STRING 0x15 /* Reference or String (Used by DEREFOF op only) */ +#define ARGI_REGION_OR_BUFFER 0x16 /* Used by LOAD op only */ +#define ARGI_DATAREFOBJ 0x17 /* Note: types above can expand to 0x1F maximum */ diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index 075d654c837f..1e4c5b6dc0b0 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -618,6 +618,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, break; case ARGI_TARGETREF: + case ARGI_STORE_TARGET: switch (destination_type) { case ACPI_TYPE_INTEGER: diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index 7b109128b035..a1afe1a1e7c2 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -209,7 +209,6 @@ acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, * (i.e., dereference the package index) * Delete the ref object, increment the returned object */ - acpi_ut_remove_reference(stack_desc); acpi_ut_add_reference(obj_desc); *stack_ptr = obj_desc; } else { diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index d2964af9ad4d..424442d50b5e 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -307,6 +307,8 @@ acpi_ex_resolve_operands(u16 opcode, case ARGI_TARGETREF: /* Allows implicit conversion rules before store */ case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */ case ARGI_SIMPLE_TARGET: /* Name, Local, or arg - no implicit conversion */ + case ARGI_STORE_TARGET: + /* * Need an operand of type ACPI_TYPE_LOCAL_REFERENCE * A Namespace Node is OK as-is diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index a7eee2400ce0..c076e9100d66 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -137,7 +137,7 @@ acpi_ex_store(union acpi_operand_object *source_desc, /* Destination is not a Reference object */ ACPI_ERROR((AE_INFO, - "Target is not a Reference or Constant object - %s [%p]", + "Target is not a Reference or Constant object - [%s] %p", acpi_ut_get_object_type_name(dest_desc), dest_desc)); @@ -189,7 +189,7 @@ acpi_ex_store(union acpi_operand_object *source_desc, * displayed and otherwise has no effect -- see ACPI Specification */ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "**** Write to Debug Object: Object %p %s ****:\n\n", + "**** Write to Debug Object: Object %p [%s] ****:\n\n", source_desc, acpi_ut_get_object_type_name(source_desc))); @@ -341,7 +341,7 @@ acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, /* All other types are invalid */ ACPI_ERROR((AE_INFO, - "Source must be Integer/Buffer/String type, not %s", + "Source must be type [Integer/Buffer/String], found [%s]", acpi_ut_get_object_type_name(source_desc))); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } @@ -352,8 +352,9 @@ acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, break; default: - ACPI_ERROR((AE_INFO, "Target is not a Package or BufferField")); - status = AE_AML_OPERAND_TYPE; + ACPI_ERROR((AE_INFO, + "Target is not of type [Package/BufferField]")); + status = AE_AML_TARGET_TYPE; break; } @@ -373,20 +374,20 @@ acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, * * DESCRIPTION: Store the object to the named object. * - * The Assignment of an object to a named object is handled here - * The value passed in will replace the current value (if any) - * with the input value. + * The assignment of an object to a named object is handled here. + * The value passed in will replace the current value (if any) + * with the input value. * - * When storing into an object the data is converted to the - * target object type then stored in the object. This means - * that the target object type (for an initialized target) will - * not be changed by a store operation. A copy_object can change - * the target type, however. + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. A copy_object can change + * the target type, however. * - * The implicit_conversion flag is set to NO/FALSE only when - * storing to an arg_x -- as per the rules of the ACPI spec. + * The implicit_conversion flag is set to NO/FALSE only when + * storing to an arg_x -- as per the rules of the ACPI spec. * - * Assumes parameters are already validated. + * Assumes parameters are already validated. * ******************************************************************************/ @@ -408,11 +409,75 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, target_type = acpi_ns_get_type(node); target_desc = acpi_ns_get_attached_object(node); - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Storing %p (%s) to node %p (%s)\n", + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Storing %p [%s] to node %p [%s]\n", source_desc, acpi_ut_get_object_type_name(source_desc), node, acpi_ut_get_type_name(target_type))); + /* Only limited target types possible for everything except copy_object */ + + if (walk_state->opcode != AML_COPY_OP) { + /* + * Only copy_object allows all object types to be overwritten. For + * target_ref(s), there are restrictions on the object types that + * are allowed. + * + * Allowable operations/typing for Store: + * + * 1) Simple Store + * Integer --> Integer (Named/Local/Arg) + * String --> String (Named/Local/Arg) + * Buffer --> Buffer (Named/Local/Arg) + * Package --> Package (Named/Local/Arg) + * + * 2) Store with implicit conversion + * Integer --> String or Buffer (Named) + * String --> Integer or Buffer (Named) + * Buffer --> Integer or String (Named) + */ + switch (target_type) { + case ACPI_TYPE_PACKAGE: + /* + * Here, can only store a package to an existing package. + * Storing a package to a Local/Arg is OK, and handled + * elsewhere. + */ + if (walk_state->opcode == AML_STORE_OP) { + if (source_desc->common.type != + ACPI_TYPE_PACKAGE) { + ACPI_ERROR((AE_INFO, + "Cannot assign type [%s] to [Package] " + "(source must be type Pkg)", + acpi_ut_get_object_type_name + (source_desc))); + + return_ACPI_STATUS(AE_AML_TARGET_TYPE); + } + break; + } + + /* Fallthrough */ + + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_REGION: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + ACPI_ERROR((AE_INFO, + "Target must be [Buffer/Integer/String/Reference], found [%s] (%4.4s)", + acpi_ut_get_type_name(node->type), + node->name.ascii)); + + return_ACPI_STATUS(AE_AML_TARGET_TYPE); + + default: + break; + } + } + /* * Resolve the source object to an actual value * (If it is a reference object) @@ -425,13 +490,13 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, /* Do the actual store operation */ switch (target_type) { - case ACPI_TYPE_INTEGER: - case ACPI_TYPE_STRING: - case ACPI_TYPE_BUFFER: /* * The simple data types all support implicit source operand * conversion before the store. */ + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: if ((walk_state->opcode == AML_COPY_OP) || !implicit_conversion) { /* @@ -467,7 +532,7 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, new_desc->common.type); ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "Store %s into %s via Convert/Attach\n", + "Store type [%s] into [%s] via Convert/Attach\n", acpi_ut_get_object_type_name (source_desc), acpi_ut_get_object_type_name @@ -491,15 +556,12 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, default: /* - * No conversions for all other types. Directly store a copy of - * the source object. This is the ACPI spec-defined behavior for - * the copy_object operator. + * copy_object operator: No conversions for all other types. + * Instead, directly store a copy of the source object. * - * NOTE: For the Store operator, this is a departure from the - * ACPI spec, which states "If conversion is impossible, abort - * the running control method". Instead, this code implements - * "If conversion is impossible, treat the Store operation as - * a CopyObject". + * This is the ACPI spec-defined behavior for the copy_object + * operator. (Note, for this default case, all normal + * Store/Target operations exited above with an error). */ status = acpi_ex_store_direct_to_node(source_desc, node, walk_state); diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c index 3101607b4efe..d1841defa669 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -122,9 +122,10 @@ acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, /* Conversion successful but still not a valid type */ ACPI_ERROR((AE_INFO, - "Cannot assign type %s to %s (must be type Int/Str/Buf)", + "Cannot assign type [%s] to [%s] (must be type Int/Str/Buf)", acpi_ut_get_object_type_name(source_desc), acpi_ut_get_type_name(target_type))); + status = AE_AML_OPERAND_TYPE; } break; @@ -275,7 +276,7 @@ acpi_ex_store_object_to_object(union acpi_operand_object *source_desc, /* * All other types come here. */ - ACPI_WARNING((AE_INFO, "Store into type %s not implemented", + ACPI_WARNING((AE_INFO, "Store into type [%s] not implemented", acpi_ut_get_object_type_name(dest_desc))); status = AE_NOT_IMPLEMENTED; diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 0eb54315b4be..0c20980bbcf3 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -226,7 +226,7 @@ acpi_ns_check_object_type(struct acpi_evaluate_info *info, { union acpi_operand_object *return_object = *return_object_ptr; acpi_status status = AE_OK; - char type_buffer[48]; /* Room for 5 types */ + char type_buffer[96]; /* Room for 10 types */ /* A Namespace node should not get here, but make sure */ diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index d452a7837c8c..ecaaaffc0788 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -232,12 +232,27 @@ char *acpi_ut_get_type_name(acpi_object_type type) char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc) { + ACPI_FUNCTION_TRACE(ut_get_object_type_name); if (!obj_desc) { - return ("[NULL Object Descriptor]"); + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Null Object Descriptor\n")); + return_PTR("[NULL Object Descriptor]"); } - return (acpi_ut_get_type_name(obj_desc->common.type)); + /* These descriptor types share a common area */ + + if ((ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) && + (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_NAMED)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Invalid object descriptor type: 0x%2.2X [%s] (%p)\n", + ACPI_GET_DESCRIPTOR_TYPE(obj_desc), + acpi_ut_get_descriptor_name(obj_desc), + obj_desc)); + + return_PTR("Invalid object"); + } + + return_PTR(acpi_ut_get_type_name(obj_desc->common.type)); } /******************************************************************************* diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h index 9f20eb4acaa6..204f5819d464 100644 --- a/include/acpi/acexcep.h +++ b/include/acpi/acexcep.h @@ -193,8 +193,9 @@ struct acpi_exception_info { #define AE_AML_ILLEGAL_ADDRESS EXCEP_AML (0x0020) #define AE_AML_INFINITE_LOOP EXCEP_AML (0x0021) #define AE_AML_UNINITIALIZED_NODE EXCEP_AML (0x0022) +#define AE_AML_TARGET_TYPE EXCEP_AML (0x0023) -#define AE_CODE_AML_MAX 0x0022 +#define AE_CODE_AML_MAX 0x0023 /* * Internal exceptions used for control @@ -358,7 +359,9 @@ static const struct acpi_exception_info acpi_gbl_exception_names_aml[] = { EXCEP_TXT("AE_AML_INFINITE_LOOP", "An apparent infinite AML While loop, method was aborted"), EXCEP_TXT("AE_AML_UNINITIALIZED_NODE", - "A namespace node is uninitialized or unresolved") + "A namespace node is uninitialized or unresolved"), + EXCEP_TXT("AE_AML_TARGET_TYPE", + "A target operand of an incorrect type was encountered") }; static const struct acpi_exception_info acpi_gbl_exception_names_ctrl[] = { -- cgit v1.2.3 From 842e71332eebf847133ea8e749c80849dae623c0 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:25:04 +0800 Subject: ACPICA: iASL: General cleanup of the file suffix #defines ACPICA commit bed456ed2976bdaafdef406b982fdf6c539befc0 Removed some extraneous defines, reordered others. Link: https://github.com/acpica/acpica/commit/bed456ed Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acapps.h | 2 +- tools/power/acpi/tools/acpidump/apfiles.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h index e9f0833e818d..e4cc48fbf4ee 100644 --- a/drivers/acpi/acpica/acapps.h +++ b/drivers/acpi/acpica/acapps.h @@ -88,7 +88,7 @@ acpi_os_printf (" %-18s%s\n", name, description); #define FILE_SUFFIX_DISASSEMBLY "dsl" -#define ACPI_TABLE_FILE_SUFFIX ".dat" +#define FILE_SUFFIX_BINARY_TABLE ".dat" /* Needs the dot */ /* * getopt diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index a37f9702b2a9..a1c62de42a3b 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -150,7 +150,7 @@ int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance) strcat(filename, instance_str); } - strcat(filename, ACPI_TABLE_FILE_SUFFIX); + strcat(filename, FILE_SUFFIX_BINARY_TABLE); if (gbl_verbose_mode) { acpi_log_error -- cgit v1.2.3 From 9957510255724c1c746c9a6264c849e9fdd4cd24 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:20 +0800 Subject: ACPICA: Linuxize: Export debugger files to Linux ACPICA commit bc2d3daa4bd429611451f28800def9fea55e63de This patch exports debugger files to Linux. Link: https://github.com/acpica/acpica/commit/bc2d3daa Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/dbcmds.c | 1187 ++++++++++++++++++++++++++++++++++++ drivers/acpi/acpica/dbconvert.c | 484 +++++++++++++++ drivers/acpi/acpica/dbdisply.c | 1108 ++++++++++++++++++++++++++++++++++ drivers/acpi/acpica/dbexec.c | 753 +++++++++++++++++++++++ drivers/acpi/acpica/dbfileio.c | 256 ++++++++ drivers/acpi/acpica/dbhistry.c | 239 ++++++++ drivers/acpi/acpica/dbinput.c | 1274 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/acpica/dbmethod.c | 369 ++++++++++++ drivers/acpi/acpica/dbnames.c | 947 +++++++++++++++++++++++++++++ drivers/acpi/acpica/dbobject.c | 533 ++++++++++++++++ drivers/acpi/acpica/dbstats.c | 546 +++++++++++++++++ drivers/acpi/acpica/dbtest.c | 1057 ++++++++++++++++++++++++++++++++ drivers/acpi/acpica/dbutils.c | 457 ++++++++++++++ drivers/acpi/acpica/dbxface.c | 467 ++++++++++++++ 14 files changed, 9677 insertions(+) create mode 100644 drivers/acpi/acpica/dbcmds.c create mode 100644 drivers/acpi/acpica/dbconvert.c create mode 100644 drivers/acpi/acpica/dbdisply.c create mode 100644 drivers/acpi/acpica/dbexec.c create mode 100644 drivers/acpi/acpica/dbfileio.c create mode 100644 drivers/acpi/acpica/dbhistry.c create mode 100644 drivers/acpi/acpica/dbinput.c create mode 100644 drivers/acpi/acpica/dbmethod.c create mode 100644 drivers/acpi/acpica/dbnames.c create mode 100644 drivers/acpi/acpica/dbobject.c create mode 100644 drivers/acpi/acpica/dbstats.c create mode 100644 drivers/acpi/acpica/dbtest.c create mode 100644 drivers/acpi/acpica/dbutils.c create mode 100644 drivers/acpi/acpica/dbxface.c (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/dbcmds.c b/drivers/acpi/acpica/dbcmds.c new file mode 100644 index 000000000000..30414b3d7fdd --- /dev/null +++ b/drivers/acpi/acpica/dbcmds.c @@ -0,0 +1,1187 @@ +/******************************************************************************* + * + * Module Name: dbcmds - Miscellaneous debug commands and output routines + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acdebug.h" +#include "acnamesp.h" +#include "acresrc.h" +#include "actables.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbcmds") + +/* Local prototypes */ +static void +acpi_dm_compare_aml_resources(u8 *aml1_buffer, + acpi_rsdesc_size aml1_buffer_length, + u8 *aml2_buffer, + acpi_rsdesc_size aml2_buffer_length); + +static acpi_status +acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name); + +static acpi_status +acpi_db_resource_callback(struct acpi_resource *resource, void *context); + +static acpi_status +acpi_db_device_resources(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +static void acpi_db_do_one_sleep_state(u8 sleep_state); + +static char *acpi_db_trace_method_name = NULL; + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_node + * + * PARAMETERS: in_string - String to convert + * + * RETURN: Pointer to a NS node + * + * DESCRIPTION: Convert a string to a valid NS pointer. Handles numeric or + * alphanumeric strings. + * + ******************************************************************************/ + +struct acpi_namespace_node *acpi_db_convert_to_node(char *in_string) +{ + struct acpi_namespace_node *node; + acpi_size address; + + if ((*in_string >= 0x30) && (*in_string <= 0x39)) { + + /* Numeric argument, convert */ + + address = strtoul(in_string, NULL, 16); + node = ACPI_TO_POINTER(address); + if (!acpi_os_readable(node, sizeof(struct acpi_namespace_node))) { + acpi_os_printf("Address %p is invalid", node); + return (NULL); + } + + /* Make sure pointer is valid NS node */ + + if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { + acpi_os_printf + ("Address %p is not a valid namespace node [%s]\n", + node, acpi_ut_get_descriptor_name(node)); + return (NULL); + } + } else { + /* + * Alpha argument: The parameter is a name string that must be + * resolved to a Namespace object. + */ + node = acpi_db_local_ns_lookup(in_string); + if (!node) { + acpi_os_printf + ("Could not find [%s] in namespace, defaulting to root node\n", + in_string); + node = acpi_gbl_root_node; + } + } + + return (node); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_sleep + * + * PARAMETERS: object_arg - Desired sleep state (0-5). NULL means + * invoke all possible sleep states. + * + * RETURN: Status + * + * DESCRIPTION: Simulate sleep/wake sequences + * + ******************************************************************************/ + +acpi_status acpi_db_sleep(char *object_arg) +{ + u8 sleep_state; + u32 i; + + ACPI_FUNCTION_TRACE(acpi_db_sleep); + + /* Null input (no arguments) means to invoke all sleep states */ + + if (!object_arg) { + acpi_os_printf("Invoking all possible sleep states, 0-%d\n", + ACPI_S_STATES_MAX); + + for (i = 0; i <= ACPI_S_STATES_MAX; i++) { + acpi_db_do_one_sleep_state((u8)i); + } + + return_ACPI_STATUS(AE_OK); + } + + /* Convert argument to binary and invoke the sleep state */ + + sleep_state = (u8)strtoul(object_arg, NULL, 0); + acpi_db_do_one_sleep_state(sleep_state); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_do_one_sleep_state + * + * PARAMETERS: sleep_state - Desired sleep state (0-5) + * + * RETURN: None + * + * DESCRIPTION: Simulate a sleep/wake sequence + * + ******************************************************************************/ + +static void acpi_db_do_one_sleep_state(u8 sleep_state) +{ + acpi_status status; + u8 sleep_type_a; + u8 sleep_type_b; + + /* Validate parameter */ + + if (sleep_state > ACPI_S_STATES_MAX) { + acpi_os_printf("Sleep state %d out of range (%d max)\n", + sleep_state, ACPI_S_STATES_MAX); + return; + } + + acpi_os_printf("\n---- Invoking sleep state S%d (%s):\n", + sleep_state, acpi_gbl_sleep_state_names[sleep_state]); + + /* Get the values for the sleep type registers (for display only) */ + + status = + acpi_get_sleep_type_data(sleep_state, &sleep_type_a, &sleep_type_b); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate [%s] method, %s\n", + acpi_gbl_sleep_state_names[sleep_state], + acpi_format_exception(status)); + return; + } + + acpi_os_printf + ("Register values for sleep state S%d: Sleep-A: %.2X, Sleep-B: %.2X\n", + sleep_state, sleep_type_a, sleep_type_b); + + /* Invoke the various sleep/wake interfaces */ + + acpi_os_printf("**** Sleep: Prepare to sleep (S%d) ****\n", + sleep_state); + status = acpi_enter_sleep_state_prep(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_os_printf("**** Sleep: Going to sleep (S%d) ****\n", sleep_state); + status = acpi_enter_sleep_state(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_os_printf("**** Wake: Prepare to return from sleep (S%d) ****\n", + sleep_state); + status = acpi_leave_sleep_state_prep(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_os_printf("**** Wake: Return from sleep (S%d) ****\n", + sleep_state); + status = acpi_leave_sleep_state(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + return; + +error_exit: + ACPI_EXCEPTION((AE_INFO, status, "During invocation of sleep state S%d", + sleep_state)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_locks + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display information about internal mutexes. + * + ******************************************************************************/ + +void acpi_db_display_locks(void) +{ + u32 i; + + for (i = 0; i < ACPI_MAX_MUTEX; i++) { + acpi_os_printf("%26s : %s\n", acpi_ut_get_mutex_name(i), + acpi_gbl_mutex_info[i].thread_id == + ACPI_MUTEX_NOT_ACQUIRED ? "Locked" : "Unlocked"); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_table_info + * + * PARAMETERS: table_arg - Name of table to be displayed + * + * RETURN: None + * + * DESCRIPTION: Display information about loaded tables. Current + * implementation displays all loaded tables. + * + ******************************************************************************/ + +void acpi_db_display_table_info(char *table_arg) +{ + u32 i; + struct acpi_table_desc *table_desc; + acpi_status status; + + /* Header */ + + acpi_os_printf("Idx ID Status Type " + "TableHeader (Sig, Address, Length, Misc)\n"); + + /* Walk the entire root table list */ + + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) { + table_desc = &acpi_gbl_root_table_list.tables[i]; + + /* Index and Table ID */ + + acpi_os_printf("%3u %.2u ", i, table_desc->owner_id); + + /* Decode the table flags */ + + if (!(table_desc->flags & ACPI_TABLE_IS_LOADED)) { + acpi_os_printf("NotLoaded "); + } else { + acpi_os_printf(" Loaded "); + } + + switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) { + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: + + acpi_os_printf("External/virtual "); + break; + + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: + + acpi_os_printf("Internal/physical "); + break; + + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + + acpi_os_printf("Internal/virtual "); + break; + + default: + + acpi_os_printf("INVALID TYPE "); + break; + } + + /* Make sure that the table is mapped */ + + status = acpi_tb_validate_table(table_desc); + if (ACPI_FAILURE(status)) { + return; + } + + /* Dump the table header */ + + if (table_desc->pointer) { + acpi_tb_print_table_header(table_desc->address, + table_desc->pointer); + } else { + /* If the pointer is null, the table has been unloaded */ + + ACPI_INFO((AE_INFO, "%4.4s - Table has been unloaded", + table_desc->signature.ascii)); + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_unload_acpi_table + * + * PARAMETERS: object_name - Namespace pathname for an object that + * is owned by the table to be unloaded + * + * RETURN: None + * + * DESCRIPTION: Unload an ACPI table, via any namespace node that is owned + * by the table. + * + ******************************************************************************/ + +void acpi_db_unload_acpi_table(char *object_name) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Translate name to an Named object */ + + node = acpi_db_convert_to_node(object_name); + if (!node) { + return; + } + + status = acpi_unload_parent_table(ACPI_CAST_PTR(acpi_handle, node)); + if (ACPI_SUCCESS(status)) { + acpi_os_printf("Parent of [%s] (%p) unloaded and uninstalled\n", + object_name, node); + } else { + acpi_os_printf("%s, while unloading parent table of [%s]\n", + acpi_format_exception(status), object_name); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_send_notify + * + * PARAMETERS: name - Name of ACPI object where to send notify + * value - Value of the notify to send. + * + * RETURN: None + * + * DESCRIPTION: Send an ACPI notification. The value specified is sent to the + * named object as an ACPI notify. + * + ******************************************************************************/ + +void acpi_db_send_notify(char *name, u32 value) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Translate name to an Named object */ + + node = acpi_db_convert_to_node(name); + if (!node) { + return; + } + + /* Dispatch the notify if legal */ + + if (acpi_ev_is_notify_object(node)) { + status = acpi_ev_queue_notify_request(node, value); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not queue notify\n"); + } + } else { + acpi_os_printf("Named object [%4.4s] Type %s, " + "must be Device/Thermal/Processor type\n", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_interfaces + * + * PARAMETERS: action_arg - Null, "install", or "remove" + * interface_name_arg - Name for install/remove options + * + * RETURN: None + * + * DESCRIPTION: Display or modify the global _OSI interface list + * + ******************************************************************************/ + +void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg) +{ + struct acpi_interface_info *next_interface; + char *sub_string; + acpi_status status; + + /* If no arguments, just display current interface list */ + + if (!action_arg) { + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, + ACPI_WAIT_FOREVER); + + next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (!(next_interface->flags & ACPI_OSI_INVALID)) { + acpi_os_printf("%s\n", next_interface->name); + } + + next_interface = next_interface->next; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return; + } + + /* If action_arg exists, so must interface_name_arg */ + + if (!interface_name_arg) { + acpi_os_printf("Missing Interface Name argument\n"); + return; + } + + /* Uppercase the action for match below */ + + acpi_ut_strupr(action_arg); + + /* install - install an interface */ + + sub_string = strstr("INSTALL", action_arg); + if (sub_string) { + status = acpi_install_interface(interface_name_arg); + if (ACPI_FAILURE(status)) { + acpi_os_printf("%s, while installing \"%s\"\n", + acpi_format_exception(status), + interface_name_arg); + } + return; + } + + /* remove - remove an interface */ + + sub_string = strstr("REMOVE", action_arg); + if (sub_string) { + status = acpi_remove_interface(interface_name_arg); + if (ACPI_FAILURE(status)) { + acpi_os_printf("%s, while removing \"%s\"\n", + acpi_format_exception(status), + interface_name_arg); + } + return; + } + + /* Invalid action_arg */ + + acpi_os_printf("Invalid action argument: %s\n", action_arg); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_template + * + * PARAMETERS: buffer_arg - Buffer name or address + * + * RETURN: None + * + * DESCRIPTION: Dump a buffer that contains a resource template + * + ******************************************************************************/ + +void acpi_db_display_template(char *buffer_arg) +{ + struct acpi_namespace_node *node; + acpi_status status; + struct acpi_buffer return_buffer; + + /* Translate buffer_arg to an Named object */ + + node = acpi_db_convert_to_node(buffer_arg); + if (!node || (node == acpi_gbl_root_node)) { + acpi_os_printf("Invalid argument: %s\n", buffer_arg); + return; + } + + /* We must have a buffer object */ + + if (node->type != ACPI_TYPE_BUFFER) { + acpi_os_printf + ("Not a Buffer object, cannot be a template: %s\n", + buffer_arg); + return; + } + + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + return_buffer.pointer = acpi_gbl_db_buffer; + + /* Attempt to convert the raw buffer to a resource list */ + + status = acpi_rs_create_resource_list(node->object, &return_buffer); + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_dbg_level |= ACPI_LV_RESOURCES; + + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not convert Buffer to a resource list: %s, %s\n", + buffer_arg, acpi_format_exception(status)); + goto dump_buffer; + } + + /* Now we can dump the resource list */ + + acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource, + return_buffer.pointer)); + +dump_buffer: + acpi_os_printf("\nRaw data buffer:\n"); + acpi_ut_debug_dump_buffer((u8 *)node->object->buffer.pointer, + node->object->buffer.length, + DB_BYTE_DISPLAY, ACPI_UINT32_MAX); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_dm_compare_aml_resources + * + * PARAMETERS: aml1_buffer - Contains first resource list + * aml1_buffer_length - Length of first resource list + * aml2_buffer - Contains second resource list + * aml2_buffer_length - Length of second resource list + * + * RETURN: None + * + * DESCRIPTION: Compare two AML resource lists, descriptor by descriptor (in + * order to isolate a miscompare to an individual resource) + * + ******************************************************************************/ + +static void +acpi_dm_compare_aml_resources(u8 *aml1_buffer, + acpi_rsdesc_size aml1_buffer_length, + u8 *aml2_buffer, + acpi_rsdesc_size aml2_buffer_length) +{ + u8 *aml1; + u8 *aml2; + u8 *aml1_end; + u8 *aml2_end; + acpi_rsdesc_size aml1_length; + acpi_rsdesc_size aml2_length; + acpi_rsdesc_size offset = 0; + u8 resource_type; + u32 count = 0; + u32 i; + + /* Compare overall buffer sizes (may be different due to size rounding) */ + + if (aml1_buffer_length != aml2_buffer_length) { + acpi_os_printf("**** Buffer length mismatch in converted " + "AML: Original %X, New %X ****\n", + aml1_buffer_length, aml2_buffer_length); + } + + aml1 = aml1_buffer; + aml2 = aml2_buffer; + aml1_end = aml1_buffer + aml1_buffer_length; + aml2_end = aml2_buffer + aml2_buffer_length; + + /* Walk the descriptor lists, comparing each descriptor */ + + while ((aml1 < aml1_end) && (aml2 < aml2_end)) { + + /* Get the lengths of each descriptor */ + + aml1_length = acpi_ut_get_descriptor_length(aml1); + aml2_length = acpi_ut_get_descriptor_length(aml2); + resource_type = acpi_ut_get_resource_type(aml1); + + /* Check for descriptor length match */ + + if (aml1_length != aml2_length) { + acpi_os_printf + ("**** Length mismatch in descriptor [%.2X] type %2.2X, " + "Offset %8.8X Len1 %X, Len2 %X ****\n", count, + resource_type, offset, aml1_length, aml2_length); + } + + /* Check for descriptor byte match */ + + else if (memcmp(aml1, aml2, aml1_length)) { + acpi_os_printf + ("**** Data mismatch in descriptor [%.2X] type %2.2X, " + "Offset %8.8X ****\n", count, resource_type, + offset); + + for (i = 0; i < aml1_length; i++) { + if (aml1[i] != aml2[i]) { + acpi_os_printf + ("Mismatch at byte offset %.2X: is %2.2X, " + "should be %2.2X\n", i, aml2[i], + aml1[i]); + } + } + } + + /* Exit on end_tag descriptor */ + + if (resource_type == ACPI_RESOURCE_NAME_END_TAG) { + return; + } + + /* Point to next descriptor in each buffer */ + + count++; + offset += aml1_length; + aml1 += aml1_length; + aml2 += aml2_length; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_dm_test_resource_conversion + * + * PARAMETERS: node - Parent device node + * name - resource method name (_CRS) + * + * RETURN: Status + * + * DESCRIPTION: Compare the original AML with a conversion of the AML to + * internal resource list, then back to AML. + * + ******************************************************************************/ + +static acpi_status +acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name) +{ + acpi_status status; + struct acpi_buffer return_buffer; + struct acpi_buffer resource_buffer; + struct acpi_buffer new_aml; + union acpi_object *original_aml; + + acpi_os_printf("Resource Conversion Comparison:\n"); + + new_aml.length = ACPI_ALLOCATE_LOCAL_BUFFER; + return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + resource_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + + /* Get the original _CRS AML resource template */ + + status = acpi_evaluate_object(node, name, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not obtain %s: %s\n", + name, acpi_format_exception(status)); + return (status); + } + + /* Get the AML resource template, converted to internal resource structs */ + + status = acpi_get_current_resources(node, &resource_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetCurrentResources failed: %s\n", + acpi_format_exception(status)); + goto exit1; + } + + /* Convert internal resource list to external AML resource template */ + + status = acpi_rs_create_aml_resources(&resource_buffer, &new_aml); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiRsCreateAmlResources failed: %s\n", + acpi_format_exception(status)); + goto exit2; + } + + /* Compare original AML to the newly created AML resource list */ + + original_aml = return_buffer.pointer; + + acpi_dm_compare_aml_resources(original_aml->buffer.pointer, + (acpi_rsdesc_size) original_aml->buffer. + length, new_aml.pointer, + (acpi_rsdesc_size) new_aml.length); + + /* Cleanup and exit */ + + ACPI_FREE(new_aml.pointer); +exit2: + ACPI_FREE(resource_buffer.pointer); +exit1: + ACPI_FREE(return_buffer.pointer); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_resource_callback + * + * PARAMETERS: acpi_walk_resource_callback + * + * RETURN: Status + * + * DESCRIPTION: Simple callback to exercise acpi_walk_resources and + * acpi_walk_resource_buffer. + * + ******************************************************************************/ + +static acpi_status +acpi_db_resource_callback(struct acpi_resource *resource, void *context) +{ + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_device_resources + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: Status + * + * DESCRIPTION: Display the _PRT/_CRS/_PRS resources for a device object. + * + ******************************************************************************/ + +static acpi_status +acpi_db_device_resources(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_namespace_node *node; + struct acpi_namespace_node *prt_node = NULL; + struct acpi_namespace_node *crs_node = NULL; + struct acpi_namespace_node *prs_node = NULL; + struct acpi_namespace_node *aei_node = NULL; + char *parent_path; + struct acpi_buffer return_buffer; + acpi_status status; + + node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + parent_path = acpi_ns_get_external_pathname(node); + if (!parent_path) { + return (AE_NO_MEMORY); + } + + /* Get handles to the resource methods for this device */ + + (void)acpi_get_handle(node, METHOD_NAME__PRT, + ACPI_CAST_PTR(acpi_handle, &prt_node)); + (void)acpi_get_handle(node, METHOD_NAME__CRS, + ACPI_CAST_PTR(acpi_handle, &crs_node)); + (void)acpi_get_handle(node, METHOD_NAME__PRS, + ACPI_CAST_PTR(acpi_handle, &prs_node)); + (void)acpi_get_handle(node, METHOD_NAME__AEI, + ACPI_CAST_PTR(acpi_handle, &aei_node)); + + if (!prt_node && !crs_node && !prs_node && !aei_node) { + goto cleanup; /* Nothing to do */ + } + + acpi_os_printf("\nDevice: %s\n", parent_path); + + /* Prepare for a return object of arbitrary size */ + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + /* _PRT */ + + if (prt_node) { + acpi_os_printf("Evaluating _PRT\n"); + + status = + acpi_evaluate_object(prt_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _PRT: %s\n", + acpi_format_exception(status)); + goto get_crs; + } + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = acpi_get_irq_routing_table(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("GetIrqRoutingTable failed: %s\n", + acpi_format_exception(status)); + goto get_crs; + } + + acpi_rs_dump_irq_list(ACPI_CAST_PTR(u8, acpi_gbl_db_buffer)); + } + + /* _CRS */ + +get_crs: + if (crs_node) { + acpi_os_printf("Evaluating _CRS\n"); + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = + acpi_evaluate_object(crs_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _CRS: %s\n", + acpi_format_exception(status)); + goto get_prs; + } + + /* This code exercises the acpi_walk_resources interface */ + + status = acpi_walk_resources(node, METHOD_NAME__CRS, + acpi_db_resource_callback, NULL); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiWalkResources failed: %s\n", + acpi_format_exception(status)); + goto get_prs; + } + + /* Get the _CRS resource list (test ALLOCATE buffer) */ + + return_buffer.pointer = NULL; + return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + + status = acpi_get_current_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetCurrentResources failed: %s\n", + acpi_format_exception(status)); + goto get_prs; + } + + /* This code exercises the acpi_walk_resource_buffer interface */ + + status = acpi_walk_resource_buffer(&return_buffer, + acpi_db_resource_callback, + NULL); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiWalkResourceBuffer failed: %s\n", + acpi_format_exception(status)); + goto end_crs; + } + + /* Dump the _CRS resource list */ + + acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource, + return_buffer. + pointer)); + + /* + * Perform comparison of original AML to newly created AML. This + * tests both the AML->Resource conversion and the Resource->AML + * conversion. + */ + (void)acpi_dm_test_resource_conversion(node, METHOD_NAME__CRS); + + /* Execute _SRS with the resource list */ + + acpi_os_printf("Evaluating _SRS\n"); + + status = acpi_set_current_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiSetCurrentResources failed: %s\n", + acpi_format_exception(status)); + goto end_crs; + } + +end_crs: + ACPI_FREE(return_buffer.pointer); + } + + /* _PRS */ + +get_prs: + if (prs_node) { + acpi_os_printf("Evaluating _PRS\n"); + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = + acpi_evaluate_object(prs_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _PRS: %s\n", + acpi_format_exception(status)); + goto get_aei; + } + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = acpi_get_possible_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetPossibleResources failed: %s\n", + acpi_format_exception(status)); + goto get_aei; + } + + acpi_rs_dump_resource_list(ACPI_CAST_PTR + (struct acpi_resource, + acpi_gbl_db_buffer)); + } + + /* _AEI */ + +get_aei: + if (aei_node) { + acpi_os_printf("Evaluating _AEI\n"); + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = + acpi_evaluate_object(aei_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _AEI: %s\n", + acpi_format_exception(status)); + goto cleanup; + } + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = acpi_get_event_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetEventResources failed: %s\n", + acpi_format_exception(status)); + goto cleanup; + } + + acpi_rs_dump_resource_list(ACPI_CAST_PTR + (struct acpi_resource, + acpi_gbl_db_buffer)); + } + +cleanup: + ACPI_FREE(parent_path); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_resources + * + * PARAMETERS: object_arg - String object name or object pointer. + * NULL or "*" means "display resources for + * all devices" + * + * RETURN: None + * + * DESCRIPTION: Display the resource objects associated with a device. + * + ******************************************************************************/ + +void acpi_db_display_resources(char *object_arg) +{ + struct acpi_namespace_node *node; + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_dbg_level |= ACPI_LV_RESOURCES; + + /* Asterisk means "display resources for all devices" */ + + if (!object_arg || (!strcmp(object_arg, "*"))) { + (void)acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_device_resources, NULL, NULL, + NULL); + } else { + /* Convert string to object pointer */ + + node = acpi_db_convert_to_node(object_arg); + if (node) { + if (node->type != ACPI_TYPE_DEVICE) { + acpi_os_printf + ("%4.4s: Name is not a device object (%s)\n", + node->name.ascii, + acpi_ut_get_type_name(node->type)); + } else { + (void)acpi_db_device_resources(node, 0, NULL, + NULL); + } + } + } + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_db_generate_gpe + * + * PARAMETERS: gpe_arg - Raw GPE number, ascii string + * block_arg - GPE block number, ascii string + * 0 or 1 for FADT GPE blocks + * + * RETURN: None + * + * DESCRIPTION: Simulate firing of a GPE + * + ******************************************************************************/ + +void acpi_db_generate_gpe(char *gpe_arg, char *block_arg) +{ + u32 block_number = 0; + u32 gpe_number; + struct acpi_gpe_event_info *gpe_event_info; + + gpe_number = strtoul(gpe_arg, NULL, 0); + + /* + * If no block arg, or block arg == 0 or 1, use the FADT-defined + * GPE blocks. + */ + if (block_arg) { + block_number = strtoul(block_arg, NULL, 0); + if (block_number == 1) { + block_number = 0; + } + } + + gpe_event_info = + acpi_ev_get_gpe_event_info(ACPI_TO_POINTER(block_number), + gpe_number); + if (!gpe_event_info) { + acpi_os_printf("Invalid GPE\n"); + return; + } + + (void)acpi_ev_gpe_dispatch(NULL, gpe_event_info, gpe_number); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_generate_sci + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Simulate an SCI -- just call the SCI dispatch. + * + ******************************************************************************/ + +void acpi_db_generate_sci(void) +{ + acpi_ev_sci_dispatch(); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ + +/******************************************************************************* + * + * FUNCTION: acpi_db_trace + * + * PARAMETERS: enable_arg - ENABLE/AML to enable tracer + * DISABLE to disable tracer + * method_arg - Method to trace + * once_arg - Whether trace once + * + * RETURN: None + * + * DESCRIPTION: Control method tracing facility + * + ******************************************************************************/ + +void acpi_db_trace(char *enable_arg, char *method_arg, char *once_arg) +{ + u32 debug_level = 0; + u32 debug_layer = 0; + u32 flags = 0; + + if (enable_arg) { + acpi_ut_strupr(enable_arg); + } + + if (once_arg) { + acpi_ut_strupr(once_arg); + } + + if (method_arg) { + if (acpi_db_trace_method_name) { + ACPI_FREE(acpi_db_trace_method_name); + acpi_db_trace_method_name = NULL; + } + + acpi_db_trace_method_name = + ACPI_ALLOCATE(strlen(method_arg) + 1); + if (!acpi_db_trace_method_name) { + acpi_os_printf("Failed to allocate method name (%s)\n", + method_arg); + return; + } + + strcpy(acpi_db_trace_method_name, method_arg); + } + + if (!strcmp(enable_arg, "ENABLE") || + !strcmp(enable_arg, "METHOD") || !strcmp(enable_arg, "OPCODE")) { + if (!strcmp(enable_arg, "ENABLE")) { + + /* Inherit current console settings */ + + debug_level = acpi_gbl_db_console_debug_level; + debug_layer = acpi_dbg_layer; + } else { + /* Restrict console output to trace points only */ + + debug_level = ACPI_LV_TRACE_POINT; + debug_layer = ACPI_EXECUTER; + } + + flags = ACPI_TRACE_ENABLED; + + if (!strcmp(enable_arg, "OPCODE")) { + flags |= ACPI_TRACE_OPCODE; + } + + if (once_arg && !strcmp(once_arg, "ONCE")) { + flags |= ACPI_TRACE_ONESHOT; + } + } + + (void)acpi_debug_trace(acpi_db_trace_method_name, + debug_level, debug_layer, flags); +} diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c new file mode 100644 index 000000000000..a71632ca8a81 --- /dev/null +++ b/drivers/acpi/acpica/dbconvert.c @@ -0,0 +1,484 @@ +/******************************************************************************* + * + * Module Name: dbconvert - debugger miscellaneous conversion routines + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbconvert") + +#define DB_DEFAULT_PKG_ELEMENTS 33 +/******************************************************************************* + * + * FUNCTION: acpi_db_hex_char_to_value + * + * PARAMETERS: hex_char - Ascii Hex digit, 0-9|a-f|A-F + * return_value - Where the converted value is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a single hex character to a 4-bit number (0-16). + * + ******************************************************************************/ +acpi_status acpi_db_hex_char_to_value(int hex_char, u8 *return_value) +{ + u8 value; + + /* Digit must be ascii [0-9a-fA-F] */ + + if (!isxdigit(hex_char)) { + return (AE_BAD_HEX_CONSTANT); + } + + if (hex_char <= 0x39) { + value = (u8)(hex_char - 0x30); + } else { + value = (u8)(toupper(hex_char) - 0x37); + } + + *return_value = value; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_hex_byte_to_binary + * + * PARAMETERS: hex_byte - Double hex digit (0x00 - 0xFF) in format: + * hi_byte then lo_byte. + * return_value - Where the converted value is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert two hex characters to an 8 bit number (0 - 255). + * + ******************************************************************************/ + +static acpi_status acpi_db_hex_byte_to_binary(char *hex_byte, u8 *return_value) +{ + u8 local0; + u8 local1; + acpi_status status; + + /* High byte */ + + status = acpi_db_hex_char_to_value(hex_byte[0], &local0); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Low byte */ + + status = acpi_db_hex_char_to_value(hex_byte[1], &local1); + if (ACPI_FAILURE(status)) { + return (status); + } + + *return_value = (u8)((local0 << 4) | local1); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_buffer + * + * PARAMETERS: string - Input string to be converted + * object - Where the buffer object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a string to a buffer object. String is treated a list + * of buffer elements, each separated by a space or comma. + * + ******************************************************************************/ + +static acpi_status +acpi_db_convert_to_buffer(char *string, union acpi_object *object) +{ + u32 i; + u32 j; + u32 length; + u8 *buffer; + acpi_status status; + + /* Generate the final buffer length */ + + for (i = 0, length = 0; string[i];) { + i += 2; + length++; + + while (string[i] && ((string[i] == ',') || (string[i] == ' '))) { + i++; + } + } + + buffer = ACPI_ALLOCATE(length); + if (!buffer) { + return (AE_NO_MEMORY); + } + + /* Convert the command line bytes to the buffer */ + + for (i = 0, j = 0; string[i];) { + status = acpi_db_hex_byte_to_binary(&string[i], &buffer[j]); + if (ACPI_FAILURE(status)) { + ACPI_FREE(buffer); + return (status); + } + + j++; + i += 2; + while (string[i] && ((string[i] == ',') || (string[i] == ' '))) { + i++; + } + } + + object->type = ACPI_TYPE_BUFFER; + object->buffer.pointer = buffer; + object->buffer.length = length; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_package + * + * PARAMETERS: string - Input string to be converted + * object - Where the package object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a string to a package object. Handles nested packages + * via recursion with acpi_db_convert_to_object. + * + ******************************************************************************/ + +acpi_status acpi_db_convert_to_package(char *string, union acpi_object * object) +{ + char *this; + char *next; + u32 i; + acpi_object_type type; + union acpi_object *elements; + acpi_status status; + + elements = + ACPI_ALLOCATE_ZEROED(DB_DEFAULT_PKG_ELEMENTS * + sizeof(union acpi_object)); + + this = string; + for (i = 0; i < (DB_DEFAULT_PKG_ELEMENTS - 1); i++) { + this = acpi_db_get_next_token(this, &next, &type); + if (!this) { + break; + } + + /* Recursive call to convert each package element */ + + status = acpi_db_convert_to_object(type, this, &elements[i]); + if (ACPI_FAILURE(status)) { + acpi_db_delete_objects(i + 1, elements); + ACPI_FREE(elements); + return (status); + } + + this = next; + } + + object->type = ACPI_TYPE_PACKAGE; + object->package.count = i; + object->package.elements = elements; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_object + * + * PARAMETERS: type - Object type as determined by parser + * string - Input string to be converted + * object - Where the new object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a typed and tokenized string to an union acpi_object. Typing: + * 1) String objects were surrounded by quotes. + * 2) Buffer objects were surrounded by parentheses. + * 3) Package objects were surrounded by brackets "[]". + * 4) All standalone tokens are treated as integers. + * + ******************************************************************************/ + +acpi_status +acpi_db_convert_to_object(acpi_object_type type, + char *string, union acpi_object * object) +{ + acpi_status status = AE_OK; + + switch (type) { + case ACPI_TYPE_STRING: + + object->type = ACPI_TYPE_STRING; + object->string.pointer = string; + object->string.length = (u32)strlen(string); + break; + + case ACPI_TYPE_BUFFER: + + status = acpi_db_convert_to_buffer(string, object); + break; + + case ACPI_TYPE_PACKAGE: + + status = acpi_db_convert_to_package(string, object); + break; + + default: + + object->type = ACPI_TYPE_INTEGER; + status = acpi_ut_strtoul64(string, 16, &object->integer.value); + break; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_encode_pld_buffer + * + * PARAMETERS: pld_info - _PLD buffer struct (Using local struct) + * + * RETURN: Encode _PLD buffer suitable for return value from _PLD + * + * DESCRIPTION: Bit-packs a _PLD buffer struct. Used to test the _PLD macros + * + ******************************************************************************/ + +u8 *acpi_db_encode_pld_buffer(struct acpi_pld_info *pld_info) +{ + u32 *buffer; + u32 dword; + + buffer = ACPI_ALLOCATE_ZEROED(ACPI_PLD_BUFFER_SIZE); + if (!buffer) { + return (NULL); + } + + /* First 32 bits */ + + dword = 0; + ACPI_PLD_SET_REVISION(&dword, pld_info->revision); + ACPI_PLD_SET_IGNORE_COLOR(&dword, pld_info->ignore_color); + ACPI_PLD_SET_RED(&dword, pld_info->red); + ACPI_PLD_SET_GREEN(&dword, pld_info->green); + ACPI_PLD_SET_BLUE(&dword, pld_info->blue); + ACPI_MOVE_32_TO_32(&buffer[0], &dword); + + /* Second 32 bits */ + + dword = 0; + ACPI_PLD_SET_WIDTH(&dword, pld_info->width); + ACPI_PLD_SET_HEIGHT(&dword, pld_info->height); + ACPI_MOVE_32_TO_32(&buffer[1], &dword); + + /* Third 32 bits */ + + dword = 0; + ACPI_PLD_SET_USER_VISIBLE(&dword, pld_info->user_visible); + ACPI_PLD_SET_DOCK(&dword, pld_info->dock); + ACPI_PLD_SET_LID(&dword, pld_info->lid); + ACPI_PLD_SET_PANEL(&dword, pld_info->panel); + ACPI_PLD_SET_VERTICAL(&dword, pld_info->vertical_position); + ACPI_PLD_SET_HORIZONTAL(&dword, pld_info->horizontal_position); + ACPI_PLD_SET_SHAPE(&dword, pld_info->shape); + ACPI_PLD_SET_ORIENTATION(&dword, pld_info->group_orientation); + ACPI_PLD_SET_TOKEN(&dword, pld_info->group_token); + ACPI_PLD_SET_POSITION(&dword, pld_info->group_position); + ACPI_PLD_SET_BAY(&dword, pld_info->bay); + ACPI_MOVE_32_TO_32(&buffer[2], &dword); + + /* Fourth 32 bits */ + + dword = 0; + ACPI_PLD_SET_EJECTABLE(&dword, pld_info->ejectable); + ACPI_PLD_SET_OSPM_EJECT(&dword, pld_info->ospm_eject_required); + ACPI_PLD_SET_CABINET(&dword, pld_info->cabinet_number); + ACPI_PLD_SET_CARD_CAGE(&dword, pld_info->card_cage_number); + ACPI_PLD_SET_REFERENCE(&dword, pld_info->reference); + ACPI_PLD_SET_ROTATION(&dword, pld_info->rotation); + ACPI_PLD_SET_ORDER(&dword, pld_info->order); + ACPI_MOVE_32_TO_32(&buffer[3], &dword); + + if (pld_info->revision >= 2) { + + /* Fifth 32 bits */ + + dword = 0; + ACPI_PLD_SET_VERT_OFFSET(&dword, pld_info->vertical_offset); + ACPI_PLD_SET_HORIZ_OFFSET(&dword, pld_info->horizontal_offset); + ACPI_MOVE_32_TO_32(&buffer[4], &dword); + } + + return (ACPI_CAST_PTR(u8, buffer)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_pld_buffer + * + * PARAMETERS: obj_desc - Object returned from _PLD method + * + * RETURN: None. + * + * DESCRIPTION: Dumps formatted contents of a _PLD return buffer. + * + ******************************************************************************/ + +#define ACPI_PLD_OUTPUT "%20s : %-6X\n" + +void acpi_db_dump_pld_buffer(union acpi_object *obj_desc) +{ + union acpi_object *buffer_desc; + struct acpi_pld_info *pld_info; + u8 *new_buffer; + acpi_status status; + + /* Object must be of type Package with at least one Buffer element */ + + if (obj_desc->type != ACPI_TYPE_PACKAGE) { + return; + } + + buffer_desc = &obj_desc->package.elements[0]; + if (buffer_desc->type != ACPI_TYPE_BUFFER) { + return; + } + + /* Convert _PLD buffer to local _PLD struct */ + + status = acpi_decode_pld_buffer(buffer_desc->buffer.pointer, + buffer_desc->buffer.length, &pld_info); + if (ACPI_FAILURE(status)) { + return; + } + + /* Encode local _PLD struct back to a _PLD buffer */ + + new_buffer = acpi_db_encode_pld_buffer(pld_info); + if (!new_buffer) { + return; + } + + /* The two bit-packed buffers should match */ + + if (memcmp(new_buffer, buffer_desc->buffer.pointer, + buffer_desc->buffer.length)) { + acpi_os_printf + ("Converted _PLD buffer does not compare. New:\n"); + + acpi_ut_dump_buffer(new_buffer, + buffer_desc->buffer.length, DB_BYTE_DISPLAY, + 0); + } + + /* First 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Revision", pld_info->revision); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_IgnoreColor", + pld_info->ignore_color); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Red", pld_info->red); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Green", pld_info->green); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Blue", pld_info->blue); + + /* Second 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Width", pld_info->width); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Height", pld_info->height); + + /* Third 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_UserVisible", + pld_info->user_visible); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Dock", pld_info->dock); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Lid", pld_info->lid); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Panel", pld_info->panel); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_VerticalPosition", + pld_info->vertical_position); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_HorizontalPosition", + pld_info->horizontal_position); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Shape", pld_info->shape); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupOrientation", + pld_info->group_orientation); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupToken", + pld_info->group_token); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupPosition", + pld_info->group_position); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Bay", pld_info->bay); + + /* Fourth 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Ejectable", pld_info->ejectable); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_EjectRequired", + pld_info->ospm_eject_required); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_CabinetNumber", + pld_info->cabinet_number); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_CardCageNumber", + pld_info->card_cage_number); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Reference", pld_info->reference); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Rotation", pld_info->rotation); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Order", pld_info->order); + + /* Fifth 32-bit dword */ + + if (buffer_desc->buffer.length > 16) { + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_VerticalOffset", + pld_info->vertical_offset); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_HorizontalOffset", + pld_info->horizontal_offset); + } + + ACPI_FREE(pld_info); + ACPI_FREE(new_buffer); +} diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c new file mode 100644 index 000000000000..672977ec7c7d --- /dev/null +++ b/drivers/acpi/acpica/dbdisply.c @@ -0,0 +1,1108 @@ +/******************************************************************************* + * + * Module Name: dbdisply - debug display commands + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acparser.h" +#include "acinterp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbdisply") + +/* Local prototypes */ +static void acpi_db_dump_parser_descriptor(union acpi_parse_object *op); + +static void *acpi_db_get_pointer(void *target); + +static acpi_status +acpi_db_display_non_root_handlers(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +/* + * System handler information. + * Used for Handlers command, in acpi_db_display_handlers. + */ +#define ACPI_PREDEFINED_PREFIX "%25s (%.2X) : " +#define ACPI_HANDLER_NAME_STRING "%30s : " +#define ACPI_HANDLER_PRESENT_STRING "%-9s (%p)\n" +#define ACPI_HANDLER_PRESENT_STRING2 "%-9s (%p)" +#define ACPI_HANDLER_NOT_PRESENT_STRING "%-9s\n" + +/* All predefined Address Space IDs */ + +static acpi_adr_space_type acpi_gbl_space_id_list[] = { + ACPI_ADR_SPACE_SYSTEM_MEMORY, + ACPI_ADR_SPACE_SYSTEM_IO, + ACPI_ADR_SPACE_PCI_CONFIG, + ACPI_ADR_SPACE_EC, + ACPI_ADR_SPACE_SMBUS, + ACPI_ADR_SPACE_CMOS, + ACPI_ADR_SPACE_PCI_BAR_TARGET, + ACPI_ADR_SPACE_IPMI, + ACPI_ADR_SPACE_GPIO, + ACPI_ADR_SPACE_GSBUS, + ACPI_ADR_SPACE_DATA_TABLE, + ACPI_ADR_SPACE_FIXED_HARDWARE +}; + +/* Global handler information */ + +typedef struct acpi_handler_info { + void *handler; + char *name; + +} acpi_handler_info; + +static struct acpi_handler_info acpi_gbl_handler_list[] = { + {&acpi_gbl_global_notify[0].handler, "System Notifications"}, + {&acpi_gbl_global_notify[1].handler, "Device Notifications"}, + {&acpi_gbl_table_handler, "ACPI Table Events"}, + {&acpi_gbl_exception_handler, "Control Method Exceptions"}, + {&acpi_gbl_interface_handler, "OSI Invocations"} +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_pointer + * + * PARAMETERS: target - Pointer to string to be converted + * + * RETURN: Converted pointer + * + * DESCRIPTION: Convert an ascii pointer value to a real value + * + ******************************************************************************/ + +static void *acpi_db_get_pointer(void *target) +{ + void *obj_ptr; + acpi_size address; + + address = strtoul(target, NULL, 16); + obj_ptr = ACPI_TO_POINTER(address); + return (obj_ptr); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_parser_descriptor + * + * PARAMETERS: op - A parser Op descriptor + * + * RETURN: None + * + * DESCRIPTION: Display a formatted parser object + * + ******************************************************************************/ + +static void acpi_db_dump_parser_descriptor(union acpi_parse_object *op) +{ + const struct acpi_opcode_info *info; + + info = acpi_ps_get_opcode_info(op->common.aml_opcode); + + acpi_os_printf("Parser Op Descriptor:\n"); + acpi_os_printf("%20.20s : %4.4X\n", "Opcode", op->common.aml_opcode); + + ACPI_DEBUG_ONLY_MEMBERS(acpi_os_printf("%20.20s : %s\n", "Opcode Name", + info->name)); + + acpi_os_printf("%20.20s : %p\n", "Value/ArgList", op->common.value.arg); + acpi_os_printf("%20.20s : %p\n", "Parent", op->common.parent); + acpi_os_printf("%20.20s : %p\n", "NextOp", op->common.next); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_and_display_object + * + * PARAMETERS: target - String with object to be displayed. Names + * and hex pointers are supported. + * output_type - Byte, Word, Dword, or Qword (B|W|D|Q) + * + * RETURN: None + * + * DESCRIPTION: Display a formatted ACPI object + * + ******************************************************************************/ + +void acpi_db_decode_and_display_object(char *target, char *output_type) +{ + void *obj_ptr; + struct acpi_namespace_node *node; + union acpi_operand_object *obj_desc; + u32 display = DB_BYTE_DISPLAY; + char buffer[80]; + struct acpi_buffer ret_buf; + acpi_status status; + u32 size; + + if (!target) { + return; + } + + /* Decode the output type */ + + if (output_type) { + acpi_ut_strupr(output_type); + if (output_type[0] == 'W') { + display = DB_WORD_DISPLAY; + } else if (output_type[0] == 'D') { + display = DB_DWORD_DISPLAY; + } else if (output_type[0] == 'Q') { + display = DB_QWORD_DISPLAY; + } + } + + ret_buf.length = sizeof(buffer); + ret_buf.pointer = buffer; + + /* Differentiate between a number and a name */ + + if ((target[0] >= 0x30) && (target[0] <= 0x39)) { + obj_ptr = acpi_db_get_pointer(target); + if (!acpi_os_readable(obj_ptr, 16)) { + acpi_os_printf + ("Address %p is invalid in this address space\n", + obj_ptr); + return; + } + + /* Decode the object type */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_ptr)) { + case ACPI_DESC_TYPE_NAMED: + + /* This is a namespace Node */ + + if (!acpi_os_readable + (obj_ptr, sizeof(struct acpi_namespace_node))) { + acpi_os_printf + ("Cannot read entire Named object at address %p\n", + obj_ptr); + return; + } + + node = obj_ptr; + goto dump_node; + + case ACPI_DESC_TYPE_OPERAND: + + /* This is a ACPI OPERAND OBJECT */ + + if (!acpi_os_readable + (obj_ptr, sizeof(union acpi_operand_object))) { + acpi_os_printf + ("Cannot read entire ACPI object at address %p\n", + obj_ptr); + return; + } + + acpi_ut_debug_dump_buffer(obj_ptr, + sizeof(union + acpi_operand_object), + display, ACPI_UINT32_MAX); + acpi_ex_dump_object_descriptor(obj_ptr, 1); + break; + + case ACPI_DESC_TYPE_PARSER: + + /* This is a Parser Op object */ + + if (!acpi_os_readable + (obj_ptr, sizeof(union acpi_parse_object))) { + acpi_os_printf + ("Cannot read entire Parser object at address %p\n", + obj_ptr); + return; + } + + acpi_ut_debug_dump_buffer(obj_ptr, + sizeof(union + acpi_parse_object), + display, ACPI_UINT32_MAX); + acpi_db_dump_parser_descriptor((union acpi_parse_object + *)obj_ptr); + break; + + default: + + /* Is not a recognizeable object */ + + acpi_os_printf + ("Not a known ACPI internal object, descriptor type %2.2X\n", + ACPI_GET_DESCRIPTOR_TYPE(obj_ptr)); + + size = 16; + if (acpi_os_readable(obj_ptr, 64)) { + size = 64; + } + + /* Just dump some memory */ + + acpi_ut_debug_dump_buffer(obj_ptr, size, display, + ACPI_UINT32_MAX); + break; + } + + return; + } + + /* The parameter is a name string that must be resolved to a Named obj */ + + node = acpi_db_local_ns_lookup(target); + if (!node) { + return; + } + +dump_node: + /* Now dump the NS node */ + + status = acpi_get_name(node, ACPI_FULL_PATHNAME_NO_TRAILING, &ret_buf); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not convert name to pathname\n"); + } + + else { + acpi_os_printf("Object (%p) Pathname: %s\n", + node, (char *)ret_buf.pointer); + } + + if (!acpi_os_readable(node, sizeof(struct acpi_namespace_node))) { + acpi_os_printf("Invalid Named object at address %p\n", node); + return; + } + + acpi_ut_debug_dump_buffer((void *)node, + sizeof(struct acpi_namespace_node), display, + ACPI_UINT32_MAX); + acpi_ex_dump_namespace_node(node, 1); + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + acpi_os_printf("\nAttached Object (%p):\n", obj_desc); + if (!acpi_os_readable + (obj_desc, sizeof(union acpi_operand_object))) { + acpi_os_printf + ("Invalid internal ACPI Object at address %p\n", + obj_desc); + return; + } + + acpi_ut_debug_dump_buffer((void *)obj_desc, + sizeof(union acpi_operand_object), + display, ACPI_UINT32_MAX); + acpi_ex_dump_object_descriptor(obj_desc, 1); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_method_info + * + * PARAMETERS: start_op - Root of the control method parse tree + * + * RETURN: None + * + * DESCRIPTION: Display information about the current method + * + ******************************************************************************/ + +void acpi_db_display_method_info(union acpi_parse_object *start_op) +{ + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + union acpi_parse_object *root_op; + union acpi_parse_object *op; + const struct acpi_opcode_info *op_info; + u32 num_ops = 0; + u32 num_operands = 0; + u32 num_operators = 0; + u32 num_remaining_ops = 0; + u32 num_remaining_operands = 0; + u32 num_remaining_operators = 0; + u8 count_remaining = FALSE; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + obj_desc = walk_state->method_desc; + node = walk_state->method_node; + + acpi_os_printf("Currently executing control method is [%4.4s]\n", + acpi_ut_get_node_name(node)); + acpi_os_printf("%X Arguments, SyncLevel = %X\n", + (u32)obj_desc->method.param_count, + (u32)obj_desc->method.sync_level); + + root_op = start_op; + while (root_op->common.parent) { + root_op = root_op->common.parent; + } + + op = root_op; + + while (op) { + if (op == start_op) { + count_remaining = TRUE; + } + + num_ops++; + if (count_remaining) { + num_remaining_ops++; + } + + /* Decode the opcode */ + + op_info = acpi_ps_get_opcode_info(op->common.aml_opcode); + switch (op_info->class) { + case AML_CLASS_ARGUMENT: + + if (count_remaining) { + num_remaining_operands++; + } + + num_operands++; + break; + + case AML_CLASS_UNKNOWN: + + /* Bad opcode or ASCII character */ + + continue; + + default: + + if (count_remaining) { + num_remaining_operators++; + } + + num_operators++; + break; + } + + op = acpi_ps_get_depth_next(start_op, op); + } + + acpi_os_printf + ("Method contains: %X AML Opcodes - %X Operators, %X Operands\n", + num_ops, num_operators, num_operands); + + acpi_os_printf + ("Remaining to execute: %X AML Opcodes - %X Operators, %X Operands\n", + num_remaining_ops, num_remaining_operators, + num_remaining_operands); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_locals + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display all locals for the currently running control method + * + ******************************************************************************/ + +void acpi_db_display_locals(void) +{ + struct acpi_walk_state *walk_state; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + acpi_db_decode_locals(walk_state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_arguments + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display all arguments for the currently running control method + * + ******************************************************************************/ + +void acpi_db_display_arguments(void) +{ + struct acpi_walk_state *walk_state; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + acpi_db_decode_arguments(walk_state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_results + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display current contents of a method result stack + * + ******************************************************************************/ + +void acpi_db_display_results(void) +{ + u32 i; + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + u32 result_count = 0; + struct acpi_namespace_node *node; + union acpi_generic_state *frame; + u32 index; /* Index onto current frame */ + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + obj_desc = walk_state->method_desc; + node = walk_state->method_node; + + if (walk_state->results) { + result_count = walk_state->result_count; + } + + acpi_os_printf("Method [%4.4s] has %X stacked result objects\n", + acpi_ut_get_node_name(node), result_count); + + /* From the top element of result stack */ + + frame = walk_state->results; + index = (result_count - 1) % ACPI_RESULTS_FRAME_OBJ_NUM; + + for (i = 0; i < result_count; i++) { + obj_desc = frame->results.obj_desc[index]; + acpi_os_printf("Result%u: ", i); + acpi_db_display_internal_object(obj_desc, walk_state); + + if (index == 0) { + frame = frame->results.next; + index = ACPI_RESULTS_FRAME_OBJ_NUM; + } + + index--; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_calling_tree + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display current calling tree of nested control methods + * + ******************************************************************************/ + +void acpi_db_display_calling_tree(void) +{ + struct acpi_walk_state *walk_state; + struct acpi_namespace_node *node; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + node = walk_state->method_node; + acpi_os_printf("Current Control Method Call Tree\n"); + + while (walk_state) { + node = walk_state->method_node; + acpi_os_printf(" [%4.4s]\n", acpi_ut_get_node_name(node)); + + walk_state = walk_state->next; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_object_type + * + * PARAMETERS: name - User entered NS node handle or name + * + * RETURN: None + * + * DESCRIPTION: Display type of an arbitrary NS node + * + ******************************************************************************/ + +void acpi_db_display_object_type(char *name) +{ + struct acpi_namespace_node *node; + struct acpi_device_info *info; + acpi_status status; + u32 i; + + node = acpi_db_convert_to_node(name); + if (!node) { + return; + } + + status = acpi_get_object_info(ACPI_CAST_PTR(acpi_handle, node), &info); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not get object info, %s\n", + acpi_format_exception(status)); + return; + } + + if (info->valid & ACPI_VALID_ADR) { + acpi_os_printf("ADR: %8.8X%8.8X, STA: %8.8X, Flags: %X\n", + ACPI_FORMAT_UINT64(info->address), + info->current_status, info->flags); + } + if (info->valid & ACPI_VALID_SXDS) { + acpi_os_printf("S1D-%2.2X S2D-%2.2X S3D-%2.2X S4D-%2.2X\n", + info->highest_dstates[0], + info->highest_dstates[1], + info->highest_dstates[2], + info->highest_dstates[3]); + } + if (info->valid & ACPI_VALID_SXWS) { + acpi_os_printf + ("S0W-%2.2X S1W-%2.2X S2W-%2.2X S3W-%2.2X S4W-%2.2X\n", + info->lowest_dstates[0], info->lowest_dstates[1], + info->lowest_dstates[2], info->lowest_dstates[3], + info->lowest_dstates[4]); + } + + if (info->valid & ACPI_VALID_HID) { + acpi_os_printf("HID: %s\n", info->hardware_id.string); + } + + if (info->valid & ACPI_VALID_UID) { + acpi_os_printf("UID: %s\n", info->unique_id.string); + } + + if (info->valid & ACPI_VALID_SUB) { + acpi_os_printf("SUB: %s\n", info->subsystem_id.string); + } + + if (info->valid & ACPI_VALID_CID) { + for (i = 0; i < info->compatible_id_list.count; i++) { + acpi_os_printf("CID %u: %s\n", i, + info->compatible_id_list.ids[i].string); + } + } + + ACPI_FREE(info); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_result_object + * + * PARAMETERS: obj_desc - Object to be displayed + * walk_state - Current walk state + * + * RETURN: None + * + * DESCRIPTION: Display the result of an AML opcode + * + * Note: Curently only displays the result object if we are single stepping. + * However, this output may be useful in other contexts and could be enabled + * to do so if needed. + * + ******************************************************************************/ + +void +acpi_db_display_result_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + + /* Only display if single stepping */ + + if (!acpi_gbl_cm_single_step) { + return; + } + + acpi_os_printf("ResultObj: "); + acpi_db_display_internal_object(obj_desc, walk_state); + acpi_os_printf("\n"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_argument_object + * + * PARAMETERS: obj_desc - Object to be displayed + * walk_state - Current walk state + * + * RETURN: None + * + * DESCRIPTION: Display the result of an AML opcode + * + ******************************************************************************/ + +void +acpi_db_display_argument_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + + if (!acpi_gbl_cm_single_step) { + return; + } + + acpi_os_printf("ArgObj: "); + acpi_db_display_internal_object(obj_desc, walk_state); +} + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_db_display_gpes + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display the current GPE structures + * + ******************************************************************************/ + +void acpi_db_display_gpes(void) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_gpe_register_info *gpe_register_info; + char *gpe_type; + struct acpi_gpe_notify_info *notify; + u32 gpe_index; + u32 block = 0; + u32 i; + u32 j; + u32 count; + char buffer[80]; + struct acpi_buffer ret_buf; + acpi_status status; + + ret_buf.length = sizeof(buffer); + ret_buf.pointer = buffer; + + block = 0; + + /* Walk the GPE lists */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + status = acpi_get_name(gpe_block->node, + ACPI_FULL_PATHNAME_NO_TRAILING, + &ret_buf); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not convert name to pathname\n"); + } + + if (gpe_block->node == acpi_gbl_fadt_gpe_device) { + gpe_type = "FADT-defined GPE block"; + } else { + gpe_type = "GPE Block Device"; + } + + acpi_os_printf + ("\nBlock %u - Info %p DeviceNode %p [%s] - %s\n", + block, gpe_block, gpe_block->node, buffer, + gpe_type); + + acpi_os_printf(" Registers: %u (%u GPEs)\n", + gpe_block->register_count, + gpe_block->gpe_count); + + acpi_os_printf + (" GPE range: 0x%X to 0x%X on interrupt %u\n", + gpe_block->block_base_number, + gpe_block->block_base_number + + (gpe_block->gpe_count - 1), + gpe_xrupt_info->interrupt_number); + + acpi_os_printf + (" RegisterInfo: %p Status %8.8X%8.8X Enable %8.8X%8.8X\n", + gpe_block->register_info, + ACPI_FORMAT_UINT64(gpe_block->register_info-> + status_address.address), + ACPI_FORMAT_UINT64(gpe_block->register_info-> + enable_address.address)); + + acpi_os_printf(" EventInfo: %p\n", + gpe_block->event_info); + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + gpe_register_info = + &gpe_block->register_info[i]; + + acpi_os_printf(" Reg %u: (GPE %.2X-%.2X) " + "RunEnable %2.2X WakeEnable %2.2X" + " Status %8.8X%8.8X Enable %8.8X%8.8X\n", + i, + gpe_register_info-> + base_gpe_number, + gpe_register_info-> + base_gpe_number + + (ACPI_GPE_REGISTER_WIDTH - 1), + gpe_register_info-> + enable_for_run, + gpe_register_info-> + enable_for_wake, + ACPI_FORMAT_UINT64 + (gpe_register_info-> + status_address.address), + ACPI_FORMAT_UINT64 + (gpe_register_info-> + enable_address.address)); + + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + gpe_index = + (i * ACPI_GPE_REGISTER_WIDTH) + j; + gpe_event_info = + &gpe_block->event_info[gpe_index]; + + if (ACPI_GPE_DISPATCH_TYPE + (gpe_event_info->flags) == + ACPI_GPE_DISPATCH_NONE) { + + /* This GPE is not used (no method or handler), ignore it */ + + continue; + } + + acpi_os_printf + (" GPE %.2X: %p RunRefs %2.2X Flags %2.2X (", + gpe_block->block_base_number + + gpe_index, gpe_event_info, + gpe_event_info->runtime_count, + gpe_event_info->flags); + + /* Decode the flags byte */ + + if (gpe_event_info-> + flags & ACPI_GPE_LEVEL_TRIGGERED) { + acpi_os_printf("Level, "); + } else { + acpi_os_printf("Edge, "); + } + + if (gpe_event_info-> + flags & ACPI_GPE_CAN_WAKE) { + acpi_os_printf("CanWake, "); + } else { + acpi_os_printf("RunOnly, "); + } + + switch (ACPI_GPE_DISPATCH_TYPE + (gpe_event_info->flags)) { + case ACPI_GPE_DISPATCH_NONE: + + acpi_os_printf("NotUsed"); + break; + + case ACPI_GPE_DISPATCH_METHOD: + + acpi_os_printf("Method"); + break; + + case ACPI_GPE_DISPATCH_HANDLER: + + acpi_os_printf("Handler"); + break; + + case ACPI_GPE_DISPATCH_NOTIFY: + + count = 0; + notify = + gpe_event_info->dispatch. + notify_list; + while (notify) { + count++; + notify = notify->next; + } + + acpi_os_printf + ("Implicit Notify on %u devices", + count); + break; + + case ACPI_GPE_DISPATCH_RAW_HANDLER: + + acpi_os_printf("RawHandler"); + break; + + default: + + acpi_os_printf("UNKNOWN: %X", + ACPI_GPE_DISPATCH_TYPE + (gpe_event_info-> + flags)); + break; + } + + acpi_os_printf(")\n"); + } + } + + block++; + gpe_block = gpe_block->next; + } + + gpe_xrupt_info = gpe_xrupt_info->next; + } +} +#endif /* !ACPI_REDUCED_HARDWARE */ + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_handlers + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display the currently installed global handlers + * + ******************************************************************************/ + +void acpi_db_display_handlers(void) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + acpi_adr_space_type space_id; + u32 i; + + /* Operation region handlers */ + + acpi_os_printf("\nOperation Region Handlers at the namespace root:\n"); + + obj_desc = acpi_ns_get_attached_object(acpi_gbl_root_node); + if (obj_desc) { + for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_gbl_space_id_list); i++) { + space_id = acpi_gbl_space_id_list[i]; + handler_obj = obj_desc->device.handler; + + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + acpi_ut_get_region_name((u8)space_id), + space_id); + + while (handler_obj) { + if (acpi_gbl_space_id_list[i] == + handler_obj->address_space.space_id) { + acpi_os_printf + (ACPI_HANDLER_PRESENT_STRING, + (handler_obj->address_space. + handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) + ? "Default" : "User", + handler_obj->address_space. + handler); + + goto found_handler; + } + + handler_obj = handler_obj->address_space.next; + } + + /* There is no handler for this space_id */ + + acpi_os_printf("None\n"); + +found_handler: ; + } + + /* Find all handlers for user-defined space_IDs */ + + handler_obj = obj_desc->device.handler; + while (handler_obj) { + if (handler_obj->address_space.space_id >= + ACPI_USER_REGION_BEGIN) { + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + "User-defined ID", + handler_obj->address_space. + space_id); + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, + (handler_obj->address_space. + handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) + ? "Default" : "User", + handler_obj->address_space. + handler); + } + + handler_obj = handler_obj->address_space.next; + } + } +#if (!ACPI_REDUCED_HARDWARE) + + /* Fixed event handlers */ + + acpi_os_printf("\nFixed Event Handlers:\n"); + + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + acpi_ut_get_event_name(i), i); + if (acpi_gbl_fixed_event_handlers[i].handler) { + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, "User", + acpi_gbl_fixed_event_handlers[i]. + handler); + } else { + acpi_os_printf(ACPI_HANDLER_NOT_PRESENT_STRING, "None"); + } + } + +#endif /* !ACPI_REDUCED_HARDWARE */ + + /* Miscellaneous global handlers */ + + acpi_os_printf("\nMiscellaneous Global Handlers:\n"); + + for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_gbl_handler_list); i++) { + acpi_os_printf(ACPI_HANDLER_NAME_STRING, + acpi_gbl_handler_list[i].name); + + if (acpi_gbl_handler_list[i].handler) { + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, "User", + acpi_gbl_handler_list[i].handler); + } else { + acpi_os_printf(ACPI_HANDLER_NOT_PRESENT_STRING, "None"); + } + } + + /* Other handlers that are installed throughout the namespace */ + + acpi_os_printf("\nOperation Region Handlers for specific devices:\n"); + + (void)acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_display_non_root_handlers, NULL, NULL, + NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_non_root_handlers + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: Status + * + * DESCRIPTION: Display information about all handlers installed for a + * device object. + * + ******************************************************************************/ + +static acpi_status +acpi_db_display_non_root_handlers(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + char *pathname; + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + if (!pathname) { + return (AE_OK); + } + + /* Display all handlers associated with this device */ + + handler_obj = obj_desc->device.handler; + while (handler_obj) { + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + acpi_ut_get_region_name((u8)handler_obj-> + address_space.space_id), + handler_obj->address_space.space_id); + + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING2, + (handler_obj->address_space.handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) ? "Default" + : "User", handler_obj->address_space.handler); + + acpi_os_printf(" Device Name: %s (%p)\n", pathname, node); + + handler_obj = handler_obj->address_space.next; + } + + ACPI_FREE(pathname); + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c new file mode 100644 index 000000000000..8eef298963a2 --- /dev/null +++ b/drivers/acpi/acpica/dbexec.c @@ -0,0 +1,753 @@ +/******************************************************************************* + * + * Module Name: dbexec - debugger control method execution + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbexec") + +static struct acpi_db_method_info acpi_gbl_db_method_info; + +/* Local prototypes */ + +static acpi_status +acpi_db_execute_method(struct acpi_db_method_info *info, + struct acpi_buffer *return_obj); + +static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info); + +static u32 acpi_db_get_outstanding_allocations(void); + +static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context); + +static acpi_status +acpi_db_execution_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +/******************************************************************************* + * + * FUNCTION: acpi_db_delete_objects + * + * PARAMETERS: count - Count of objects in the list + * objects - Array of ACPI_OBJECTs to be deleted + * + * RETURN: None + * + * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested + * packages via recursion. + * + ******************************************************************************/ + +void acpi_db_delete_objects(u32 count, union acpi_object *objects) +{ + u32 i; + + for (i = 0; i < count; i++) { + switch (objects[i].type) { + case ACPI_TYPE_BUFFER: + + ACPI_FREE(objects[i].buffer.pointer); + break; + + case ACPI_TYPE_PACKAGE: + + /* Recursive call to delete package elements */ + + acpi_db_delete_objects(objects[i].package.count, + objects[i].package.elements); + + /* Free the elements array */ + + ACPI_FREE(objects[i].package.elements); + break; + + default: + + break; + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_method + * + * PARAMETERS: info - Valid info segment + * return_obj - Where to put return object + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method. + * + ******************************************************************************/ + +static acpi_status +acpi_db_execute_method(struct acpi_db_method_info *info, + struct acpi_buffer *return_obj) +{ + acpi_status status; + struct acpi_object_list param_objects; + union acpi_object params[ACPI_DEBUGGER_MAX_ARGS + 1]; + u32 i; + + ACPI_FUNCTION_TRACE(db_execute_method); + + if (acpi_gbl_db_output_to_file && !acpi_dbg_level) { + acpi_os_printf("Warning: debug output is not enabled!\n"); + } + + param_objects.count = 0; + param_objects.pointer = NULL; + + /* Pass through any command-line arguments */ + + if (info->args && info->args[0]) { + + /* Get arguments passed on the command line */ + + for (i = 0; (info->args[i] && *(info->args[i])); i++) { + + /* Convert input string (token) to an actual union acpi_object */ + + status = acpi_db_convert_to_object(info->types[i], + info->args[i], + ¶ms[i]); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While parsing method arguments")); + goto cleanup; + } + } + + param_objects.count = i; + param_objects.pointer = params; + } + + /* Prepare for a return object of arbitrary size */ + + return_obj->pointer = acpi_gbl_db_buffer; + return_obj->length = ACPI_DEBUG_BUFFER_SIZE; + + /* Do the actual method execution */ + + acpi_gbl_method_executing = TRUE; + status = acpi_evaluate_object(NULL, info->pathname, + ¶m_objects, return_obj); + + acpi_gbl_cm_single_step = FALSE; + acpi_gbl_method_executing = FALSE; + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "while executing %s from debugger", + info->pathname)); + + if (status == AE_BUFFER_OVERFLOW) { + ACPI_ERROR((AE_INFO, + "Possible overflow of internal debugger " + "buffer (size 0x%X needed 0x%X)", + ACPI_DEBUG_BUFFER_SIZE, + (u32)return_obj->length)); + } + } + +cleanup: + acpi_db_delete_objects(param_objects.count, params); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_setup + * + * PARAMETERS: info - Valid method info + * + * RETURN: None + * + * DESCRIPTION: Setup info segment prior to method execution + * + ******************************************************************************/ + +static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info) +{ + acpi_status status; + + ACPI_FUNCTION_NAME(db_execute_setup); + + /* Catenate the current scope to the supplied name */ + + info->pathname[0] = 0; + if ((info->name[0] != '\\') && (info->name[0] != '/')) { + if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), + acpi_gbl_db_scope_buf)) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + } + + if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), + info->name)) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + + acpi_db_prep_namestring(info->pathname); + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("Evaluating %s\n", info->pathname); + + if (info->flags & EX_SINGLE_STEP) { + acpi_gbl_cm_single_step = TRUE; + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + } + + else { + /* No single step, allow redirection to a file */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + } + + return (AE_OK); + +error_exit: + + ACPI_EXCEPTION((AE_INFO, status, "During setup for method execution")); + return (status); +} + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS +u32 acpi_db_get_cache_info(struct acpi_memory_list *cache) +{ + + return (cache->total_allocated - cache->total_freed - + cache->current_depth); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_outstanding_allocations + * + * PARAMETERS: None + * + * RETURN: Current global allocation count minus cache entries + * + * DESCRIPTION: Determine the current number of "outstanding" allocations -- + * those allocations that have not been freed and also are not + * in one of the various object caches. + * + ******************************************************************************/ + +static u32 acpi_db_get_outstanding_allocations(void) +{ + u32 outstanding = 0; + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + + outstanding += acpi_db_get_cache_info(acpi_gbl_state_cache); + outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_cache); + outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_ext_cache); + outstanding += acpi_db_get_cache_info(acpi_gbl_operand_cache); +#endif + + return (outstanding); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execution_walk + * + * PARAMETERS: WALK_CALLBACK + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method. Name is relative to the current + * scope. + * + ******************************************************************************/ + +static acpi_status +acpi_db_execution_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + struct acpi_buffer return_obj; + acpi_status status; + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc->method.param_count) { + return (AE_OK); + } + + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + acpi_ns_print_node_pathname(node, "Evaluating"); + + /* Do the actual method execution */ + + acpi_os_printf("\n"); + acpi_gbl_method_executing = TRUE; + + status = acpi_evaluate_object(node, NULL, NULL, &return_obj); + + acpi_os_printf("Evaluation of [%4.4s] returned %s\n", + acpi_ut_get_node_name(node), + acpi_format_exception(status)); + + acpi_gbl_method_executing = FALSE; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute + * + * PARAMETERS: name - Name of method to execute + * args - Parameters to the method + * Types - + * flags - single step/no single step + * + * RETURN: None + * + * DESCRIPTION: Execute a control method. Name is relative to the current + * scope. + * + ******************************************************************************/ + +void +acpi_db_execute(char *name, char **args, acpi_object_type * types, u32 flags) +{ + acpi_status status; + struct acpi_buffer return_obj; + char *name_string; + +#ifdef ACPI_DEBUG_OUTPUT + u32 previous_allocations; + u32 allocations; + + /* Memory allocation tracking */ + + previous_allocations = acpi_db_get_outstanding_allocations(); +#endif + + if (*name == '*') { + (void)acpi_walk_namespace(ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_execution_walk, NULL, NULL, + NULL); + return; + } else { + name_string = ACPI_ALLOCATE(strlen(name) + 1); + if (!name_string) { + return; + } + + memset(&acpi_gbl_db_method_info, 0, + sizeof(struct acpi_db_method_info)); + + strcpy(name_string, name); + acpi_ut_strupr(name_string); + acpi_gbl_db_method_info.name = name_string; + acpi_gbl_db_method_info.args = args; + acpi_gbl_db_method_info.types = types; + acpi_gbl_db_method_info.flags = flags; + + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + status = acpi_db_execute_setup(&acpi_gbl_db_method_info); + if (ACPI_FAILURE(status)) { + ACPI_FREE(name_string); + return; + } + + /* Get the NS node, determines existence also */ + + status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, + &acpi_gbl_db_method_info.method); + if (ACPI_SUCCESS(status)) { + status = + acpi_db_execute_method(&acpi_gbl_db_method_info, + &return_obj); + } + ACPI_FREE(name_string); + } + + /* + * Allow any handlers in separate threads to complete. + * (Such as Notify handlers invoked from AML executed above). + */ + acpi_os_sleep((u64)10); + +#ifdef ACPI_DEBUG_OUTPUT + + /* Memory allocation tracking */ + + allocations = + acpi_db_get_outstanding_allocations() - previous_allocations; + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + + if (allocations > 0) { + acpi_os_printf + ("0x%X Outstanding allocations after evaluation of %s\n", + allocations, acpi_gbl_db_method_info.pathname); + } +#endif + + if (ACPI_FAILURE(status)) { + acpi_os_printf("Evaluation of %s failed with status %s\n", + acpi_gbl_db_method_info.pathname, + acpi_format_exception(status)); + } else { + /* Display a return object, if any */ + + if (return_obj.length) { + acpi_os_printf("Evaluation of %s returned object %p, " + "external buffer length %X\n", + acpi_gbl_db_method_info.pathname, + return_obj.pointer, + (u32)return_obj.length); + + acpi_db_dump_external_object(return_obj.pointer, 1); + + /* Dump a _PLD buffer if present */ + + if (ACPI_COMPARE_NAME + ((ACPI_CAST_PTR + (struct acpi_namespace_node, + acpi_gbl_db_method_info.method)->name.ascii), + METHOD_NAME__PLD)) { + acpi_db_dump_pld_buffer(return_obj.pointer); + } + } else { + acpi_os_printf + ("No object was returned from evaluation of %s\n", + acpi_gbl_db_method_info.pathname); + } + } + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_method_thread + * + * PARAMETERS: context - Execution info segment + * + * RETURN: None + * + * DESCRIPTION: Debugger execute thread. Waits for a command line, then + * simply dispatches it. + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context) +{ + acpi_status status; + struct acpi_db_method_info *info = context; + struct acpi_db_method_info local_info; + u32 i; + u8 allow; + struct acpi_buffer return_obj; + + /* + * acpi_gbl_db_method_info.Arguments will be passed as method arguments. + * Prevent acpi_gbl_db_method_info from being modified by multiple threads + * concurrently. + * + * Note: The arguments we are passing are used by the ASL test suite + * (aslts). Do not change them without updating the tests. + */ + (void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER); + + if (info->init_args) { + acpi_db_uint32_to_hex_string(info->num_created, + info->index_of_thread_str); + acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(), + info->id_of_thread_str); + } + + if (info->threads && (info->num_created < info->num_threads)) { + info->threads[info->num_created++] = acpi_os_get_thread_id(); + } + + local_info = *info; + local_info.args = local_info.arguments; + local_info.arguments[0] = local_info.num_threads_str; + local_info.arguments[1] = local_info.id_of_thread_str; + local_info.arguments[2] = local_info.index_of_thread_str; + local_info.arguments[3] = NULL; + + local_info.types = local_info.arg_types; + + (void)acpi_os_signal_semaphore(info->info_gate, 1); + + for (i = 0; i < info->num_loops; i++) { + status = acpi_db_execute_method(&local_info, &return_obj); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("%s During evaluation of %s at iteration %X\n", + acpi_format_exception(status), info->pathname, i); + if (status == AE_ABORT_METHOD) { + break; + } + } +#if 0 + if ((i % 100) == 0) { + acpi_os_printf("%u loops, Thread 0x%x\n", + i, acpi_os_get_thread_id()); + } + + if (return_obj.length) { + acpi_os_printf + ("Evaluation of %s returned object %p Buflen %X\n", + info->pathname, return_obj.pointer, + (u32)return_obj.length); + acpi_db_dump_external_object(return_obj.pointer, 1); + } +#endif + } + + /* Signal our completion */ + + allow = 0; + (void)acpi_os_wait_semaphore(info->thread_complete_gate, + 1, ACPI_WAIT_FOREVER); + info->num_completed++; + + if (info->num_completed == info->num_threads) { + + /* Do signal for main thread once only */ + allow = 1; + } + + (void)acpi_os_signal_semaphore(info->thread_complete_gate, 1); + + if (allow) { + status = acpi_os_signal_semaphore(info->main_thread_gate, 1); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not signal debugger thread sync semaphore, %s\n", + acpi_format_exception(status)); + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_create_execution_threads + * + * PARAMETERS: num_threads_arg - Number of threads to create + * num_loops_arg - Loop count for the thread(s) + * method_name_arg - Control method to execute + * + * RETURN: None + * + * DESCRIPTION: Create threads to execute method(s) + * + ******************************************************************************/ + +void +acpi_db_create_execution_threads(char *num_threads_arg, + char *num_loops_arg, char *method_name_arg) +{ + acpi_status status; + u32 num_threads; + u32 num_loops; + u32 i; + u32 size; + acpi_mutex main_thread_gate; + acpi_mutex thread_complete_gate; + acpi_mutex info_gate; + + /* Get the arguments */ + + num_threads = strtoul(num_threads_arg, NULL, 0); + num_loops = strtoul(num_loops_arg, NULL, 0); + + if (!num_threads || !num_loops) { + acpi_os_printf("Bad argument: Threads %X, Loops %X\n", + num_threads, num_loops); + return; + } + + /* + * Create the semaphore for synchronization of + * the created threads with the main thread. + */ + status = acpi_os_create_semaphore(1, 0, &main_thread_gate); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not create semaphore for " + "synchronization with the main thread, %s\n", + acpi_format_exception(status)); + return; + } + + /* + * Create the semaphore for synchronization + * between the created threads. + */ + status = acpi_os_create_semaphore(1, 1, &thread_complete_gate); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not create semaphore for " + "synchronization between the created threads, %s\n", + acpi_format_exception(status)); + + (void)acpi_os_delete_semaphore(main_thread_gate); + return; + } + + status = acpi_os_create_semaphore(1, 1, &info_gate); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not create semaphore for " + "synchronization of AcpiGbl_DbMethodInfo, %s\n", + acpi_format_exception(status)); + + (void)acpi_os_delete_semaphore(thread_complete_gate); + (void)acpi_os_delete_semaphore(main_thread_gate); + return; + } + + memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); + + /* Array to store IDs of threads */ + + acpi_gbl_db_method_info.num_threads = num_threads; + size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads; + + acpi_gbl_db_method_info.threads = acpi_os_allocate(size); + if (acpi_gbl_db_method_info.threads == NULL) { + acpi_os_printf("No memory for thread IDs array\n"); + (void)acpi_os_delete_semaphore(main_thread_gate); + (void)acpi_os_delete_semaphore(thread_complete_gate); + (void)acpi_os_delete_semaphore(info_gate); + return; + } + memset(acpi_gbl_db_method_info.threads, 0, size); + + /* Setup the context to be passed to each thread */ + + acpi_gbl_db_method_info.name = method_name_arg; + acpi_gbl_db_method_info.flags = 0; + acpi_gbl_db_method_info.num_loops = num_loops; + acpi_gbl_db_method_info.main_thread_gate = main_thread_gate; + acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate; + acpi_gbl_db_method_info.info_gate = info_gate; + + /* Init arguments to be passed to method */ + + acpi_gbl_db_method_info.init_args = 1; + acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments; + acpi_gbl_db_method_info.arguments[0] = + acpi_gbl_db_method_info.num_threads_str; + acpi_gbl_db_method_info.arguments[1] = + acpi_gbl_db_method_info.id_of_thread_str; + acpi_gbl_db_method_info.arguments[2] = + acpi_gbl_db_method_info.index_of_thread_str; + acpi_gbl_db_method_info.arguments[3] = NULL; + + acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types; + acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER; + acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER; + acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER; + + acpi_db_uint32_to_hex_string(num_threads, + acpi_gbl_db_method_info.num_threads_str); + + status = acpi_db_execute_setup(&acpi_gbl_db_method_info); + if (ACPI_FAILURE(status)) { + goto cleanup_and_exit; + } + + /* Get the NS node, determines existence also */ + + status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, + &acpi_gbl_db_method_info.method); + if (ACPI_FAILURE(status)) { + acpi_os_printf("%s Could not get handle for %s\n", + acpi_format_exception(status), + acpi_gbl_db_method_info.pathname); + goto cleanup_and_exit; + } + + /* Create the threads */ + + acpi_os_printf("Creating %X threads to execute %X times each\n", + num_threads, num_loops); + + for (i = 0; i < (num_threads); i++) { + status = + acpi_os_execute(OSL_DEBUGGER_THREAD, acpi_db_method_thread, + &acpi_gbl_db_method_info); + if (ACPI_FAILURE(status)) { + break; + } + } + + /* Wait for all threads to complete */ + + (void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER); + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("All threads (%X) have completed\n", num_threads); + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + +cleanup_and_exit: + + /* Cleanup and exit */ + + (void)acpi_os_delete_semaphore(main_thread_gate); + (void)acpi_os_delete_semaphore(thread_complete_gate); + (void)acpi_os_delete_semaphore(info_gate); + + acpi_os_free(acpi_gbl_db_method_info.threads); + acpi_gbl_db_method_info.threads = NULL; +} diff --git a/drivers/acpi/acpica/dbfileio.c b/drivers/acpi/acpica/dbfileio.c new file mode 100644 index 000000000000..d0e6b20ce82a --- /dev/null +++ b/drivers/acpi/acpica/dbfileio.c @@ -0,0 +1,256 @@ +/******************************************************************************* + * + * Module Name: dbfileio - Debugger file I/O commands. These can't usually + * be used when running the debugger in Ring 0 (Kernel mode) + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "actables.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbfileio") + +#ifdef ACPI_DEBUGGER +/******************************************************************************* + * + * FUNCTION: acpi_db_close_debug_file + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: If open, close the current debug output file + * + ******************************************************************************/ +void acpi_db_close_debug_file(void) +{ + +#ifdef ACPI_APPLICATION + + if (acpi_gbl_debug_file) { + fclose(acpi_gbl_debug_file); + acpi_gbl_debug_file = NULL; + acpi_gbl_db_output_to_file = FALSE; + acpi_os_printf("Debug output file %s closed\n", + acpi_gbl_db_debug_filename); + } +#endif +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_open_debug_file + * + * PARAMETERS: name - Filename to open + * + * RETURN: None + * + * DESCRIPTION: Open a file where debug output will be directed. + * + ******************************************************************************/ + +void acpi_db_open_debug_file(char *name) +{ + +#ifdef ACPI_APPLICATION + + acpi_db_close_debug_file(); + acpi_gbl_debug_file = fopen(name, "w+"); + if (!acpi_gbl_debug_file) { + acpi_os_printf("Could not open debug file %s\n", name); + return; + } + + acpi_os_printf("Debug output file %s opened\n", name); + strncpy(acpi_gbl_db_debug_filename, name, + sizeof(acpi_gbl_db_debug_filename)); + acpi_gbl_db_output_to_file = TRUE; + +#endif +} +#endif + +#ifdef ACPI_APPLICATION +#include "acapps.h" + +/******************************************************************************* + * + * FUNCTION: ae_local_load_table + * + * PARAMETERS: table - pointer to a buffer containing the entire + * table to be loaded + * + * RETURN: Status + * + * DESCRIPTION: This function is called to load a table from the caller's + * buffer. The buffer must contain an entire ACPI Table including + * a valid header. The header fields will be verified, and if it + * is determined that the table is invalid, the call will fail. + * + ******************************************************************************/ + +static acpi_status ae_local_load_table(struct acpi_table_header *table) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ae_local_load_table); + +#if 0 +/* struct acpi_table_desc table_info; */ + + if (!table) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + table_info.pointer = table; + status = acpi_tb_recognize_table(&table_info, ACPI_TABLE_ALL); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Install the new table into the local data structures */ + + status = acpi_tb_init_table_descriptor(&table_info); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + + /* Table already exists, no error */ + + status = AE_OK; + } + + /* Free table allocated by acpi_tb_get_table */ + + acpi_tb_delete_single_table(&table_info); + return_ACPI_STATUS(status); + } +#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) + + status = + acpi_ns_load_table(table_info.installed_desc, acpi_gbl_root_node); + if (ACPI_FAILURE(status)) { + + /* Uninstall table and free the buffer */ + + acpi_tb_delete_tables_by_type(ACPI_TABLE_ID_DSDT); + return_ACPI_STATUS(status); + } +#endif +#endif + + return_ACPI_STATUS(status); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_table_from_file + * + * PARAMETERS: filename - File where table is located + * return_table - Where a pointer to the table is returned + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table from a file + * + ******************************************************************************/ + +acpi_status +acpi_db_get_table_from_file(char *filename, + struct acpi_table_header **return_table, + u8 must_be_aml_file) +{ +#ifdef ACPI_APPLICATION + acpi_status status; + struct acpi_table_header *table; + u8 is_aml_table = TRUE; + + status = acpi_ut_read_table_from_file(filename, &table); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (must_be_aml_file) { + is_aml_table = acpi_ut_is_aml_table(table); + if (!is_aml_table) { + ACPI_EXCEPTION((AE_INFO, AE_OK, + "Input for -e is not an AML table: " + "\"%4.4s\" (must be DSDT/SSDT)", + table->signature)); + return (AE_TYPE); + } + } + + if (is_aml_table) { + + /* Attempt to recognize and install the table */ + + status = ae_local_load_table(table); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + acpi_os_printf + ("Table %4.4s is already installed\n", + table->signature); + } else { + acpi_os_printf("Could not install table, %s\n", + acpi_format_exception(status)); + } + + return (status); + } + + acpi_tb_print_table_header(0, table); + + fprintf(stderr, + "Acpi table [%4.4s] successfully installed and loaded\n", + table->signature); + } + + acpi_gbl_acpi_hardware_present = FALSE; + if (return_table) { + *return_table = table; + } + +#endif /* ACPI_APPLICATION */ + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbhistry.c b/drivers/acpi/acpica/dbhistry.c new file mode 100644 index 000000000000..9c66a9eadd38 --- /dev/null +++ b/drivers/acpi/acpica/dbhistry.c @@ -0,0 +1,239 @@ +/****************************************************************************** + * + * Module Name: dbhistry - debugger HISTORY command + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbhistry") + +#define HI_NO_HISTORY 0 +#define HI_RECORD_HISTORY 1 +#define HISTORY_SIZE 40 +typedef struct history_info { + char *command; + u32 cmd_num; + +} HISTORY_INFO; + +static HISTORY_INFO acpi_gbl_history_buffer[HISTORY_SIZE]; +static u16 acpi_gbl_lo_history = 0; +static u16 acpi_gbl_num_history = 0; +static u16 acpi_gbl_next_history_index = 0; +u32 acpi_gbl_next_cmd_num = 1; + +/******************************************************************************* + * + * FUNCTION: acpi_db_add_to_history + * + * PARAMETERS: command_line - Command to add + * + * RETURN: None + * + * DESCRIPTION: Add a command line to the history buffer. + * + ******************************************************************************/ + +void acpi_db_add_to_history(char *command_line) +{ + u16 cmd_len; + u16 buffer_len; + + /* Put command into the next available slot */ + + cmd_len = (u16)strlen(command_line); + if (!cmd_len) { + return; + } + + if (acpi_gbl_history_buffer[acpi_gbl_next_history_index].command != + NULL) { + buffer_len = + (u16) + strlen(acpi_gbl_history_buffer[acpi_gbl_next_history_index]. + command); + + if (cmd_len > buffer_len) { + acpi_os_free(acpi_gbl_history_buffer + [acpi_gbl_next_history_index].command); + acpi_gbl_history_buffer[acpi_gbl_next_history_index]. + command = acpi_os_allocate(cmd_len + 1); + } + } else { + acpi_gbl_history_buffer[acpi_gbl_next_history_index].command = + acpi_os_allocate(cmd_len + 1); + } + + strcpy(acpi_gbl_history_buffer[acpi_gbl_next_history_index].command, + command_line); + + acpi_gbl_history_buffer[acpi_gbl_next_history_index].cmd_num = + acpi_gbl_next_cmd_num; + + /* Adjust indexes */ + + if ((acpi_gbl_num_history == HISTORY_SIZE) && + (acpi_gbl_next_history_index == acpi_gbl_lo_history)) { + acpi_gbl_lo_history++; + if (acpi_gbl_lo_history >= HISTORY_SIZE) { + acpi_gbl_lo_history = 0; + } + } + + acpi_gbl_next_history_index++; + if (acpi_gbl_next_history_index >= HISTORY_SIZE) { + acpi_gbl_next_history_index = 0; + } + + acpi_gbl_next_cmd_num++; + if (acpi_gbl_num_history < HISTORY_SIZE) { + acpi_gbl_num_history++; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_history + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display the contents of the history buffer + * + ******************************************************************************/ + +void acpi_db_display_history(void) +{ + u32 i; + u16 history_index; + + history_index = acpi_gbl_lo_history; + + /* Dump entire history buffer */ + + for (i = 0; i < acpi_gbl_num_history; i++) { + if (acpi_gbl_history_buffer[history_index].command) { + acpi_os_printf("%3ld %s\n", + acpi_gbl_history_buffer[history_index]. + cmd_num, + acpi_gbl_history_buffer[history_index]. + command); + } + + history_index++; + if (history_index >= HISTORY_SIZE) { + history_index = 0; + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_from_history + * + * PARAMETERS: command_num_arg - String containing the number of the + * command to be retrieved + * + * RETURN: Pointer to the retrieved command. Null on error. + * + * DESCRIPTION: Get a command from the history buffer + * + ******************************************************************************/ + +char *acpi_db_get_from_history(char *command_num_arg) +{ + u32 cmd_num; + + if (command_num_arg == NULL) { + cmd_num = acpi_gbl_next_cmd_num - 1; + } + + else { + cmd_num = strtoul(command_num_arg, NULL, 0); + } + + return (acpi_db_get_history_by_index(cmd_num)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_history_by_index + * + * PARAMETERS: cmd_num - Index of the desired history entry. + * Values are 0...(acpi_gbl_next_cmd_num - 1) + * + * RETURN: Pointer to the retrieved command. Null on error. + * + * DESCRIPTION: Get a command from the history buffer + * + ******************************************************************************/ + +char *acpi_db_get_history_by_index(u32 cmd_num) +{ + u32 i; + u16 history_index; + + /* Search history buffer */ + + history_index = acpi_gbl_lo_history; + for (i = 0; i < acpi_gbl_num_history; i++) { + if (acpi_gbl_history_buffer[history_index].cmd_num == cmd_num) { + + /* Found the command, return it */ + + return (acpi_gbl_history_buffer[history_index].command); + } + + /* History buffer is circular */ + + history_index++; + if (history_index >= HISTORY_SIZE) { + history_index = 0; + } + } + + acpi_os_printf("Invalid history number: %u\n", history_index); + return (NULL); +} diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c new file mode 100644 index 000000000000..7f1b6ec62668 --- /dev/null +++ b/drivers/acpi/acpica/dbinput.c @@ -0,0 +1,1274 @@ +/******************************************************************************* + * + * Module Name: dbinput - user front-end to the AML debugger + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbinput") + +/* Local prototypes */ +static u32 acpi_db_get_line(char *input_buffer); + +static u32 acpi_db_match_command(char *user_command); + +static void acpi_db_single_thread(void); + +static void acpi_db_display_command_info(char *command, u8 display_all); + +static void acpi_db_display_help(char *command); + +static u8 +acpi_db_match_command_help(char *command, + const struct acpi_db_command_help *help); + +/* + * Top-level debugger commands. + * + * This list of commands must match the string table below it + */ +enum acpi_ex_debugger_commands { + CMD_NOT_FOUND = 0, + CMD_NULL, + CMD_ALLOCATIONS, + CMD_ARGS, + CMD_ARGUMENTS, + CMD_BREAKPOINT, + CMD_BUSINFO, + CMD_CALL, + CMD_DEBUG, + CMD_DISASSEMBLE, + CMD_DISASM, + CMD_DUMP, + CMD_EVALUATE, + CMD_EXECUTE, + CMD_EXIT, + CMD_FIND, + CMD_GO, + CMD_HANDLERS, + CMD_HELP, + CMD_HELP2, + CMD_HISTORY, + CMD_HISTORY_EXE, + CMD_HISTORY_LAST, + CMD_INFORMATION, + CMD_INTEGRITY, + CMD_INTO, + CMD_LEVEL, + CMD_LIST, + CMD_LOCALS, + CMD_LOCKS, + CMD_METHODS, + CMD_NAMESPACE, + CMD_NOTIFY, + CMD_OBJECTS, + CMD_OSI, + CMD_OWNER, + CMD_PATHS, + CMD_PREDEFINED, + CMD_PREFIX, + CMD_QUIT, + CMD_REFERENCES, + CMD_RESOURCES, + CMD_RESULTS, + CMD_SET, + CMD_STATS, + CMD_STOP, + CMD_TABLES, + CMD_TEMPLATE, + CMD_TRACE, + CMD_TREE, + CMD_TYPE, +#ifdef ACPI_APPLICATION + CMD_ENABLEACPI, + CMD_EVENT, + CMD_GPE, + CMD_GPES, + CMD_SCI, + CMD_SLEEP, + + CMD_CLOSE, + CMD_LOAD, + CMD_OPEN, + CMD_UNLOAD, + + CMD_TERMINATE, + CMD_THREADS, + + CMD_TEST, +#endif +}; + +#define CMD_FIRST_VALID 2 + +/* Second parameter is the required argument count */ + +static const struct acpi_db_command_info acpi_gbl_db_commands[] = { + {"", 0}, + {"", 0}, + {"ALLOCATIONS", 0}, + {"ARGS", 0}, + {"ARGUMENTS", 0}, + {"BREAKPOINT", 1}, + {"BUSINFO", 0}, + {"CALL", 0}, + {"DEBUG", 1}, + {"DISASSEMBLE", 1}, + {"DISASM", 1}, + {"DUMP", 1}, + {"EVALUATE", 1}, + {"EXECUTE", 1}, + {"EXIT", 0}, + {"FIND", 1}, + {"GO", 0}, + {"HANDLERS", 0}, + {"HELP", 0}, + {"?", 0}, + {"HISTORY", 0}, + {"!", 1}, + {"!!", 0}, + {"INFORMATION", 0}, + {"INTEGRITY", 0}, + {"INTO", 0}, + {"LEVEL", 0}, + {"LIST", 0}, + {"LOCALS", 0}, + {"LOCKS", 0}, + {"METHODS", 0}, + {"NAMESPACE", 0}, + {"NOTIFY", 2}, + {"OBJECTS", 0}, + {"OSI", 0}, + {"OWNER", 1}, + {"PATHS", 0}, + {"PREDEFINED", 0}, + {"PREFIX", 0}, + {"QUIT", 0}, + {"REFERENCES", 1}, + {"RESOURCES", 0}, + {"RESULTS", 0}, + {"SET", 3}, + {"STATS", 1}, + {"STOP", 0}, + {"TABLES", 0}, + {"TEMPLATE", 1}, + {"TRACE", 1}, + {"TREE", 0}, + {"TYPE", 1}, +#ifdef ACPI_APPLICATION + {"ENABLEACPI", 0}, + {"EVENT", 1}, + {"GPE", 1}, + {"GPES", 0}, + {"SCI", 0}, + {"SLEEP", 0}, + + {"CLOSE", 0}, + {"LOAD", 1}, + {"OPEN", 1}, + {"UNLOAD", 1}, + + {"TERMINATE", 0}, + {"THREADS", 3}, + + {"TEST", 1}, +#endif + {NULL, 0} +}; + +/* + * Help for all debugger commands. First argument is the number of lines + * of help to output for the command. + */ +static const struct acpi_db_command_help acpi_gbl_db_command_help[] = { + {0, "\nGeneral-Purpose Commands:", "\n"}, + {1, " Allocations", "Display list of current memory allocations\n"}, + {2, " Dump
|", "\n"}, + {0, " [Byte|Word|Dword|Qword]", + "Display ACPI objects or memory\n"}, + {1, " Handlers", "Info about global handlers\n"}, + {1, " Help [Command]", "This help screen or individual command\n"}, + {1, " History", "Display command history buffer\n"}, + {1, " Level ] [console]", + "Get/Set debug level for file or console\n"}, + {1, " Locks", "Current status of internal mutexes\n"}, + {1, " Osi [Install|Remove ]", + "Display or modify global _OSI list\n"}, + {1, " Quit or Exit", "Exit this command\n"}, + {8, " Stats ", + "Display namespace and memory statistics\n"}, + {1, " Allocations", "Display list of current memory allocations\n"}, + {1, " Memory", "Dump internal memory lists\n"}, + {1, " Misc", "Namespace search and mutex stats\n"}, + {1, " Objects", "Summary of namespace objects\n"}, + {1, " Sizes", "Sizes for each of the internal objects\n"}, + {1, " Stack", "Display CPU stack usage\n"}, + {1, " Tables", "Info about current ACPI table(s)\n"}, + {1, " Tables", "Display info about loaded ACPI tables\n"}, + {1, " ! ", "Execute command from history buffer\n"}, + {1, " !!", "Execute last command again\n"}, + + {0, "\nNamespace Access Commands:", "\n"}, + {1, " Businfo", "Display system bus info\n"}, + {1, " Disassemble ", "Disassemble a control method\n"}, + {1, " Find (? is wildcard)", + "Find ACPI name(s) with wildcards\n"}, + {1, " Integrity", "Validate namespace integrity\n"}, + {1, " Methods", "Display list of loaded control methods\n"}, + {1, " Namespace [Object] [Depth]", + "Display loaded namespace tree/subtree\n"}, + {1, " Notify ", "Send a notification on Object\n"}, + {1, " Objects [ObjectType]", + "Display summary of all objects or just given type\n"}, + {1, " Owner [Depth]", + "Display loaded namespace by object owner\n"}, + {1, " Paths", "Display full pathnames of namespace objects\n"}, + {1, " Predefined", "Check all predefined names\n"}, + {1, " Prefix []", "Set or Get current execution prefix\n"}, + {1, " References ", "Find all references to object at addr\n"}, + {1, " Resources [DeviceName]", + "Display Device resources (no arg = all devices)\n"}, + {1, " Set N ", "Set value for named integer\n"}, + {1, " Template ", "Format/dump a Buffer/ResourceTemplate\n"}, + {1, " Type ", "Display object type\n"}, + + {0, "\nControl Method Execution Commands:", "\n"}, + {1, " Arguments (or Args)", "Display method arguments\n"}, + {1, " Breakpoint ", "Set an AML execution breakpoint\n"}, + {1, " Call", "Run to next control method invocation\n"}, + {1, " Debug [Arguments]", "Single Step a control method\n"}, + {6, " Evaluate", "Synonym for Execute\n"}, + {5, " Execute [Arguments]", "Execute control method\n"}, + {1, " Hex Integer", "Integer method argument\n"}, + {1, " \"Ascii String\"", "String method argument\n"}, + {1, " (Hex Byte List)", "Buffer method argument\n"}, + {1, " [Package Element List]", "Package method argument\n"}, + {1, " Go", "Allow method to run to completion\n"}, + {1, " Information", "Display info about the current method\n"}, + {1, " Into", "Step into (not over) a method call\n"}, + {1, " List [# of Aml Opcodes]", "Display method ASL statements\n"}, + {1, " Locals", "Display method local variables\n"}, + {1, " Results", "Display method result stack\n"}, + {1, " Set <#> ", "Set method data (Arguments/Locals)\n"}, + {1, " Stop", "Terminate control method\n"}, + {5, " Trace [] [Once]", + "Trace control method execution\n"}, + {1, " Enable", "Enable all messages\n"}, + {1, " Disable", "Disable tracing\n"}, + {1, " Method", "Enable method execution messages\n"}, + {1, " Opcode", "Enable opcode execution messages\n"}, + {1, " Tree", "Display control method calling tree\n"}, + {1, " ", "Single step next AML opcode (over calls)\n"}, + +#ifdef ACPI_APPLICATION + {0, "\nHardware Simulation Commands:", "\n"}, + {1, " EnableAcpi", "Enable ACPI (hardware) mode\n"}, + {1, " Event ", "Generate AcpiEvent (Fixed/GPE)\n"}, + {1, " Gpe [GpeBlockDevice]", "Simulate a GPE\n"}, + {1, " Gpes", "Display info on all GPE devices\n"}, + {1, " Sci", "Generate an SCI\n"}, + {1, " Sleep [SleepState]", "Simulate sleep/wake sequence(s) (0-5)\n"}, + + {0, "\nFile I/O Commands:", "\n"}, + {1, " Close", "Close debug output file\n"}, + {1, " Load ", "Load ACPI table from a file\n"}, + {1, " Open ", "Open a file for debug output\n"}, + {1, " Unload ", + "Unload an ACPI table via namespace object\n"}, + + {0, "\nUser Space Commands:", "\n"}, + {1, " Terminate", "Delete namespace and all internal objects\n"}, + {1, " Thread ", + "Spawn threads to execute method(s)\n"}, + + {0, "\nDebug Test Commands:", "\n"}, + {3, " Test ", "Invoke a debug test\n"}, + {1, " Objects", "Read/write/compare all namespace data objects\n"}, + {1, " Predefined", + "Execute all ACPI predefined names (_STA, etc.)\n"}, +#endif + {0, NULL, NULL} +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_match_command_help + * + * PARAMETERS: command - Command string to match + * help - Help table entry to attempt match + * + * RETURN: TRUE if command matched, FALSE otherwise + * + * DESCRIPTION: Attempt to match a command in the help table in order to + * print help information for a single command. + * + ******************************************************************************/ + +static u8 +acpi_db_match_command_help(char *command, + const struct acpi_db_command_help *help) +{ + char *invocation = help->invocation; + u32 line_count; + + /* Valid commands in the help table begin with a couple of spaces */ + + if (*invocation != ' ') { + return (FALSE); + } + + while (*invocation == ' ') { + invocation++; + } + + /* Match command name (full command or substring) */ + + while ((*command) && (*invocation) && (*invocation != ' ')) { + if (tolower((int)*command) != tolower((int)*invocation)) { + return (FALSE); + } + + invocation++; + command++; + } + + /* Print the appropriate number of help lines */ + + line_count = help->line_count; + while (line_count) { + acpi_os_printf("%-38s : %s", help->invocation, + help->description); + help++; + line_count--; + } + + return (TRUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_command_info + * + * PARAMETERS: command - Command string to match + * display_all - Display all matching commands, or just + * the first one (substring match) + * + * RETURN: None + * + * DESCRIPTION: Display help information for a Debugger command. + * + ******************************************************************************/ + +static void acpi_db_display_command_info(char *command, u8 display_all) +{ + const struct acpi_db_command_help *next; + u8 matched; + + next = acpi_gbl_db_command_help; + while (next->invocation) { + matched = acpi_db_match_command_help(command, next); + if (!display_all && matched) { + return; + } + + next++; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_help + * + * PARAMETERS: command - Optional command string to display help. + * if not specified, all debugger command + * help strings are displayed + * + * RETURN: None + * + * DESCRIPTION: Display help for a single debugger command, or all of them. + * + ******************************************************************************/ + +static void acpi_db_display_help(char *command) +{ + const struct acpi_db_command_help *next = acpi_gbl_db_command_help; + + if (!command) { + + /* No argument to help, display help for all commands */ + + while (next->invocation) { + acpi_os_printf("%-38s%s", next->invocation, + next->description); + next++; + } + } else { + /* Display help for all commands that match the subtring */ + + acpi_db_display_command_info(command, TRUE); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_next_token + * + * PARAMETERS: string - Command buffer + * next - Return value, end of next token + * + * RETURN: Pointer to the start of the next token. + * + * DESCRIPTION: Command line parsing. Get the next token on the command line + * + ******************************************************************************/ + +char *acpi_db_get_next_token(char *string, + char **next, acpi_object_type * return_type) +{ + char *start; + u32 depth; + acpi_object_type type = ACPI_TYPE_INTEGER; + + /* At end of buffer? */ + + if (!string || !(*string)) { + return (NULL); + } + + /* Remove any spaces at the beginning */ + + if (*string == ' ') { + while (*string && (*string == ' ')) { + string++; + } + + if (!(*string)) { + return (NULL); + } + } + + switch (*string) { + case '"': + + /* This is a quoted string, scan until closing quote */ + + string++; + start = string; + type = ACPI_TYPE_STRING; + + /* Find end of string */ + + while (*string && (*string != '"')) { + string++; + } + break; + + case '(': + + /* This is the start of a buffer, scan until closing paren */ + + string++; + start = string; + type = ACPI_TYPE_BUFFER; + + /* Find end of buffer */ + + while (*string && (*string != ')')) { + string++; + } + break; + + case '[': + + /* This is the start of a package, scan until closing bracket */ + + string++; + depth = 1; + start = string; + type = ACPI_TYPE_PACKAGE; + + /* Find end of package (closing bracket) */ + + while (*string) { + + /* Handle String package elements */ + + if (*string == '"') { + /* Find end of string */ + + string++; + while (*string && (*string != '"')) { + string++; + } + if (!(*string)) { + break; + } + } else if (*string == '[') { + depth++; /* A nested package declaration */ + } else if (*string == ']') { + depth--; + if (depth == 0) { /* Found final package closing bracket */ + break; + } + } + + string++; + } + break; + + default: + + start = string; + + /* Find end of token */ + + while (*string && (*string != ' ')) { + string++; + } + break; + } + + if (!(*string)) { + *next = NULL; + } else { + *string = 0; + *next = string + 1; + } + + *return_type = type; + return (start); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_line + * + * PARAMETERS: input_buffer - Command line buffer + * + * RETURN: Count of arguments to the command + * + * DESCRIPTION: Get the next command line from the user. Gets entire line + * up to the next newline + * + ******************************************************************************/ + +static u32 acpi_db_get_line(char *input_buffer) +{ + u32 i; + u32 count; + char *next; + char *this; + + if (acpi_ut_safe_strcpy + (acpi_gbl_db_parsed_buf, sizeof(acpi_gbl_db_parsed_buf), + input_buffer)) { + acpi_os_printf + ("Buffer overflow while parsing input line (max %u characters)\n", + sizeof(acpi_gbl_db_parsed_buf)); + return (0); + } + + this = acpi_gbl_db_parsed_buf; + for (i = 0; i < ACPI_DEBUGGER_MAX_ARGS; i++) { + acpi_gbl_db_args[i] = acpi_db_get_next_token(this, &next, + &acpi_gbl_db_arg_types + [i]); + if (!acpi_gbl_db_args[i]) { + break; + } + + this = next; + } + + /* Uppercase the actual command */ + + if (acpi_gbl_db_args[0]) { + acpi_ut_strupr(acpi_gbl_db_args[0]); + } + + count = i; + if (count) { + count--; /* Number of args only */ + } + + return (count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_match_command + * + * PARAMETERS: user_command - User command line + * + * RETURN: Index into command array, -1 if not found + * + * DESCRIPTION: Search command array for a command match + * + ******************************************************************************/ + +static u32 acpi_db_match_command(char *user_command) +{ + u32 i; + + if (!user_command || user_command[0] == 0) { + return (CMD_NULL); + } + + for (i = CMD_FIRST_VALID; acpi_gbl_db_commands[i].name; i++) { + if (strstr(acpi_gbl_db_commands[i].name, user_command) == + acpi_gbl_db_commands[i].name) { + return (i); + } + } + + /* Command not recognized */ + + return (CMD_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_command_dispatch + * + * PARAMETERS: input_buffer - Command line buffer + * walk_state - Current walk + * op - Current (executing) parse op + * + * RETURN: Status + * + * DESCRIPTION: Command dispatcher. + * + ******************************************************************************/ + +acpi_status +acpi_db_command_dispatch(char *input_buffer, + struct acpi_walk_state * walk_state, + union acpi_parse_object * op) +{ + u32 temp; + u32 command_index; + u32 param_count; + char *command_line; + acpi_status status = AE_CTRL_TRUE; + + /* If acpi_terminate has been called, terminate this thread */ + + if (acpi_gbl_db_terminate_threads) { + return (AE_CTRL_TERMINATE); + } + + /* Find command and add to the history buffer */ + + param_count = acpi_db_get_line(input_buffer); + command_index = acpi_db_match_command(acpi_gbl_db_args[0]); + temp = 0; + + /* + * We don't want to add the !! command to the history buffer. It + * would cause an infinite loop because it would always be the + * previous command. + */ + if (command_index != CMD_HISTORY_LAST) { + acpi_db_add_to_history(input_buffer); + } + + /* Verify that we have the minimum number of params */ + + if (param_count < acpi_gbl_db_commands[command_index].min_args) { + acpi_os_printf + ("%u parameters entered, [%s] requires %u parameters\n", + param_count, acpi_gbl_db_commands[command_index].name, + acpi_gbl_db_commands[command_index].min_args); + + acpi_db_display_command_info(acpi_gbl_db_commands + [command_index].name, FALSE); + return (AE_CTRL_TRUE); + } + + /* Decode and dispatch the command */ + + switch (command_index) { + case CMD_NULL: + + if (op) { + return (AE_OK); + } + break; + + case CMD_ALLOCATIONS: + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_ut_dump_allocations((u32)-1, NULL); +#endif + break; + + case CMD_ARGS: + case CMD_ARGUMENTS: + + acpi_db_display_arguments(); + break; + + case CMD_BREAKPOINT: + + acpi_db_set_method_breakpoint(acpi_gbl_db_args[1], walk_state, + op); + break; + + case CMD_BUSINFO: + + acpi_db_get_bus_info(); + break; + + case CMD_CALL: + + acpi_db_set_method_call_breakpoint(op); + status = AE_OK; + break; + + case CMD_DEBUG: + + acpi_db_execute(acpi_gbl_db_args[1], + &acpi_gbl_db_args[2], &acpi_gbl_db_arg_types[2], + EX_SINGLE_STEP); + break; + + case CMD_DISASSEMBLE: + case CMD_DISASM: + + (void)acpi_db_disassemble_method(acpi_gbl_db_args[1]); + break; + + case CMD_DUMP: + + acpi_db_decode_and_display_object(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_EVALUATE: + case CMD_EXECUTE: + + acpi_db_execute(acpi_gbl_db_args[1], + &acpi_gbl_db_args[2], &acpi_gbl_db_arg_types[2], + EX_NO_SINGLE_STEP); + break; + + case CMD_FIND: + + status = acpi_db_find_name_in_namespace(acpi_gbl_db_args[1]); + break; + + case CMD_GO: + + acpi_gbl_cm_single_step = FALSE; + return (AE_OK); + + case CMD_HANDLERS: + + acpi_db_display_handlers(); + break; + + case CMD_HELP: + case CMD_HELP2: + + acpi_db_display_help(acpi_gbl_db_args[1]); + break; + + case CMD_HISTORY: + + acpi_db_display_history(); + break; + + case CMD_HISTORY_EXE: /* ! command */ + + command_line = acpi_db_get_from_history(acpi_gbl_db_args[1]); + if (!command_line) { + return (AE_CTRL_TRUE); + } + + status = acpi_db_command_dispatch(command_line, walk_state, op); + return (status); + + case CMD_HISTORY_LAST: /* !! command */ + + command_line = acpi_db_get_from_history(NULL); + if (!command_line) { + return (AE_CTRL_TRUE); + } + + status = acpi_db_command_dispatch(command_line, walk_state, op); + return (status); + + case CMD_INFORMATION: + + acpi_db_display_method_info(op); + break; + + case CMD_INTEGRITY: + + acpi_db_check_integrity(); + break; + + case CMD_INTO: + + if (op) { + acpi_gbl_cm_single_step = TRUE; + return (AE_OK); + } + break; + + case CMD_LEVEL: + + if (param_count == 0) { + acpi_os_printf + ("Current debug level for file output is: %8.8lX\n", + acpi_gbl_db_debug_level); + acpi_os_printf + ("Current debug level for console output is: %8.8lX\n", + acpi_gbl_db_console_debug_level); + } else if (param_count == 2) { + temp = acpi_gbl_db_console_debug_level; + acpi_gbl_db_console_debug_level = + strtoul(acpi_gbl_db_args[1], NULL, 16); + acpi_os_printf + ("Debug Level for console output was %8.8lX, now %8.8lX\n", + temp, acpi_gbl_db_console_debug_level); + } else { + temp = acpi_gbl_db_debug_level; + acpi_gbl_db_debug_level = + strtoul(acpi_gbl_db_args[1], NULL, 16); + acpi_os_printf + ("Debug Level for file output was %8.8lX, now %8.8lX\n", + temp, acpi_gbl_db_debug_level); + } + break; + + case CMD_LIST: + + acpi_db_disassemble_aml(acpi_gbl_db_args[1], op); + break; + + case CMD_LOCKS: + + acpi_db_display_locks(); + break; + + case CMD_LOCALS: + + acpi_db_display_locals(); + break; + + case CMD_METHODS: + + status = acpi_db_display_objects("METHOD", acpi_gbl_db_args[1]); + break; + + case CMD_NAMESPACE: + + acpi_db_dump_namespace(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_NOTIFY: + + temp = strtoul(acpi_gbl_db_args[2], NULL, 0); + acpi_db_send_notify(acpi_gbl_db_args[1], temp); + break; + + case CMD_OBJECTS: + + acpi_ut_strupr(acpi_gbl_db_args[1]); + status = + acpi_db_display_objects(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_OSI: + + acpi_db_display_interfaces(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_OWNER: + + acpi_db_dump_namespace_by_owner(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_PATHS: + + acpi_db_dump_namespace_paths(); + break; + + case CMD_PREFIX: + + acpi_db_set_scope(acpi_gbl_db_args[1]); + break; + + case CMD_REFERENCES: + + acpi_db_find_references(acpi_gbl_db_args[1]); + break; + + case CMD_RESOURCES: + + acpi_db_display_resources(acpi_gbl_db_args[1]); + break; + + case CMD_RESULTS: + + acpi_db_display_results(); + break; + + case CMD_SET: + + acpi_db_set_method_data(acpi_gbl_db_args[1], + acpi_gbl_db_args[2], + acpi_gbl_db_args[3]); + break; + + case CMD_STATS: + + status = acpi_db_display_statistics(acpi_gbl_db_args[1]); + break; + + case CMD_STOP: + + return (AE_NOT_IMPLEMENTED); + + case CMD_TABLES: + + acpi_db_display_table_info(acpi_gbl_db_args[1]); + break; + + case CMD_TEMPLATE: + + acpi_db_display_template(acpi_gbl_db_args[1]); + break; + + case CMD_TRACE: + + acpi_db_trace(acpi_gbl_db_args[1], acpi_gbl_db_args[2], + acpi_gbl_db_args[3]); + break; + + case CMD_TREE: + + acpi_db_display_calling_tree(); + break; + + case CMD_TYPE: + + acpi_db_display_object_type(acpi_gbl_db_args[1]); + break; + +#ifdef ACPI_APPLICATION + + /* Hardware simulation commands. */ + + case CMD_ENABLEACPI: +#if (!ACPI_REDUCED_HARDWARE) + + status = acpi_enable(); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiEnable failed (Status=%X)\n", + status); + return (status); + } +#endif /* !ACPI_REDUCED_HARDWARE */ + break; + + case CMD_EVENT: + + acpi_os_printf("Event command not implemented\n"); + break; + + case CMD_GPE: + + acpi_db_generate_gpe(acpi_gbl_db_args[1], acpi_gbl_db_args[2]); + break; + + case CMD_GPES: + + acpi_db_display_gpes(); + break; + + case CMD_SCI: + + acpi_db_generate_sci(); + break; + + case CMD_SLEEP: + + status = acpi_db_sleep(acpi_gbl_db_args[1]); + break; + + /* File I/O commands. */ + + case CMD_CLOSE: + + acpi_db_close_debug_file(); + break; + + case CMD_LOAD: + + status = + acpi_db_get_table_from_file(acpi_gbl_db_args[1], NULL, + FALSE); + break; + + case CMD_OPEN: + + acpi_db_open_debug_file(acpi_gbl_db_args[1]); + break; + + /* User space commands. */ + + case CMD_TERMINATE: + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ut_subsystem_shutdown(); + + /* + * TBD: [Restructure] Need some way to re-initialize without + * re-creating the semaphores! + */ + + /* acpi_initialize (NULL); */ + break; + + case CMD_THREADS: + + acpi_db_create_execution_threads(acpi_gbl_db_args[1], + acpi_gbl_db_args[2], + acpi_gbl_db_args[3]); + break; + + /* Debug test commands. */ + + case CMD_PREDEFINED: + + acpi_db_check_predefined_names(); + break; + + case CMD_TEST: + + acpi_db_execute_test(acpi_gbl_db_args[1]); + break; + + case CMD_UNLOAD: + + acpi_db_unload_acpi_table(acpi_gbl_db_args[1]); + break; +#endif + + case CMD_EXIT: + case CMD_QUIT: + + if (op) { + acpi_os_printf("Method execution terminated\n"); + return (AE_CTRL_TERMINATE); + } + + if (!acpi_gbl_db_output_to_file) { + acpi_dbg_level = ACPI_DEBUG_DEFAULT; + } +#ifdef ACPI_APPLICATION + acpi_db_close_debug_file(); +#endif + acpi_gbl_db_terminate_threads = TRUE; + return (AE_CTRL_TERMINATE); + + case CMD_NOT_FOUND: + default: + + acpi_os_printf("%s: unknown command\n", acpi_gbl_db_args[0]); + return (AE_CTRL_TRUE); + } + + if (ACPI_SUCCESS(status)) { + status = AE_CTRL_TRUE; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_thread + * + * PARAMETERS: context - Not used + * + * RETURN: None + * + * DESCRIPTION: Debugger execute thread. Waits for a command line, then + * simply dispatches it. + * + ******************************************************************************/ + +void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context) +{ + acpi_status status = AE_OK; + acpi_status Mstatus; + + while (status != AE_CTRL_TERMINATE) { + acpi_gbl_method_executing = FALSE; + acpi_gbl_step_to_next_call = FALSE; + + Mstatus = acpi_os_acquire_mutex(acpi_gbl_db_command_ready, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(Mstatus)) { + return; + } + + status = + acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL, NULL); + + acpi_os_release_mutex(acpi_gbl_db_command_complete); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_single_thread + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Debugger execute thread. Waits for a command line, then + * simply dispatches it. + * + ******************************************************************************/ + +static void acpi_db_single_thread(void) +{ + + acpi_gbl_method_executing = FALSE; + acpi_gbl_step_to_next_call = FALSE; + + (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL, NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_user_commands + * + * PARAMETERS: prompt - User prompt (depends on mode) + * op - Current executing parse op + * + * RETURN: None + * + * DESCRIPTION: Command line execution for the AML debugger. Commands are + * matched and dispatched here. + * + ******************************************************************************/ + +acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op) +{ + acpi_status status = AE_OK; + + acpi_os_printf("\n"); + + /* TBD: [Restructure] Need a separate command line buffer for step mode */ + + while (!acpi_gbl_db_terminate_threads) { + + /* Force output to console until a command is entered */ + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + + /* Different prompt if method is executing */ + + if (!acpi_gbl_method_executing) { + acpi_os_printf("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT); + } else { + acpi_os_printf("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT); + } + + /* Get the user input line */ + + status = acpi_os_get_line(acpi_gbl_db_line_buf, + ACPI_DB_LINE_BUFFER_SIZE, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While parsing command line")); + return (status); + } + + /* Check for single or multithreaded debug */ + + if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { + /* + * Signal the debug thread that we have a command to execute, + * and wait for the command to complete. + */ + acpi_os_release_mutex(acpi_gbl_db_command_ready); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = + acpi_os_acquire_mutex(acpi_gbl_db_command_complete, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + } else { + /* Just call to the command line interpreter */ + + acpi_db_single_thread(); + } + } + + /* Shut down the debugger */ + + acpi_terminate_debugger(); + + /* + * Only this thread (the original thread) should actually terminate the + * subsystem, because all the semaphores are deleted during termination + */ + status = acpi_terminate(); + return (status); +} diff --git a/drivers/acpi/acpica/dbmethod.c b/drivers/acpi/acpica/dbmethod.c new file mode 100644 index 000000000000..01e5a71147fd --- /dev/null +++ b/drivers/acpi/acpica/dbmethod.c @@ -0,0 +1,369 @@ +/******************************************************************************* + * + * Module Name: dbmethod - Debug commands for control methods + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acdebug.h" +#include "acparser.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbmethod") + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_method_breakpoint + * + * PARAMETERS: location - AML offset of breakpoint + * walk_state - Current walk info + * op - Current Op (from parse walk) + * + * RETURN: None + * + * DESCRIPTION: Set a breakpoint in a control method at the specified + * AML offset + * + ******************************************************************************/ +void +acpi_db_set_method_breakpoint(char *location, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + u32 address; + u32 aml_offset; + + if (!op) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + /* Get and verify the breakpoint address */ + + address = strtoul(location, NULL, 16); + aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml, + walk_state->parser_state.aml_start); + if (address <= aml_offset) { + acpi_os_printf("Breakpoint %X is beyond current address %X\n", + address, aml_offset); + } + + /* Save breakpoint in current walk */ + + walk_state->user_breakpoint = address; + acpi_os_printf("Breakpoint set at AML offset %X\n", address); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_method_call_breakpoint + * + * PARAMETERS: op - Current Op (from parse walk) + * + * RETURN: None + * + * DESCRIPTION: Set a breakpoint in a control method at the specified + * AML offset + * + ******************************************************************************/ + +void acpi_db_set_method_call_breakpoint(union acpi_parse_object *op) +{ + + if (!op) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + acpi_gbl_step_to_next_call = TRUE; +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_method_data + * + * PARAMETERS: type_arg - L for local, A for argument + * index_arg - which one + * value_arg - Value to set. + * + * RETURN: None + * + * DESCRIPTION: Set a local or argument for the running control method. + * NOTE: only object supported is Number. + * + ******************************************************************************/ + +void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg) +{ + char type; + u32 index; + u32 value; + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + acpi_status status; + struct acpi_namespace_node *node; + + /* Validate type_arg */ + + acpi_ut_strupr(type_arg); + type = type_arg[0]; + if ((type != 'L') && (type != 'A') && (type != 'N')) { + acpi_os_printf("Invalid SET operand: %s\n", type_arg); + return; + } + + value = strtoul(value_arg, NULL, 16); + + if (type == 'N') { + node = acpi_db_convert_to_node(index_arg); + if (!node) { + return; + } + + if (node->type != ACPI_TYPE_INTEGER) { + acpi_os_printf("Can only set Integer nodes\n"); + return; + } + obj_desc = node->object; + obj_desc->integer.value = value; + return; + } + + /* Get the index and value */ + + index = strtoul(index_arg, NULL, 16); + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + /* Create and initialize the new object */ + + obj_desc = acpi_ut_create_integer_object((u64)value); + if (!obj_desc) { + acpi_os_printf("Could not create an internal object\n"); + return; + } + + /* Store the new object into the target */ + + switch (type) { + case 'A': + + /* Set a method argument */ + + if (index > ACPI_METHOD_MAX_ARG) { + acpi_os_printf("Arg%u - Invalid argument name\n", + index); + goto cleanup; + } + + status = acpi_ds_store_object_to_local(ACPI_REFCLASS_ARG, + index, obj_desc, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + obj_desc = walk_state->arguments[index].object; + + acpi_os_printf("Arg%u: ", index); + acpi_db_display_internal_object(obj_desc, walk_state); + break; + + case 'L': + + /* Set a method local */ + + if (index > ACPI_METHOD_MAX_LOCAL) { + acpi_os_printf + ("Local%u - Invalid local variable name\n", index); + goto cleanup; + } + + status = acpi_ds_store_object_to_local(ACPI_REFCLASS_LOCAL, + index, obj_desc, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + obj_desc = walk_state->local_variables[index].object; + + acpi_os_printf("Local%u: ", index); + acpi_db_display_internal_object(obj_desc, walk_state); + break; + + default: + + break; + } + +cleanup: + acpi_ut_remove_reference(obj_desc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_disassemble_aml + * + * PARAMETERS: statements - Number of statements to disassemble + * op - Current Op (from parse walk) + * + * RETURN: None + * + * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number + * of statements specified. + * + ******************************************************************************/ + +void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op) +{ + u32 num_statements = 8; + + if (!op) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + if (statements) { + num_statements = strtoul(statements, NULL, 0); + } +#ifdef ACPI_DISASSEMBLER + acpi_dm_disassemble(NULL, op, num_statements); +#endif +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_disassemble_method + * + * PARAMETERS: name - Name of control method + * + * RETURN: None + * + * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number + * of statements specified. + * + ******************************************************************************/ + +acpi_status acpi_db_disassemble_method(char *name) +{ + acpi_status status; + union acpi_parse_object *op; + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *method; + + method = acpi_db_convert_to_node(name); + if (!method) { + return (AE_BAD_PARAMETER); + } + + if (method->type != ACPI_TYPE_METHOD) { + ACPI_ERROR((AE_INFO, "%s (%s): Object must be a control method", + name, acpi_ut_get_type_name(method->type))); + return (AE_BAD_PARAMETER); + } + + obj_desc = method->object; + + op = acpi_ps_create_scope_op(obj_desc->method.aml_start); + if (!op) { + return (AE_NO_MEMORY); + } + + /* Create and initialize a new walk state */ + + walk_state = acpi_ds_create_walk_state(0, op, NULL, NULL); + if (!walk_state) { + return (AE_NO_MEMORY); + } + + status = acpi_ds_init_aml_walk(walk_state, op, NULL, + obj_desc->method.aml_start, + obj_desc->method.aml_length, NULL, + ACPI_IMODE_LOAD_PASS1); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = acpi_ut_allocate_owner_id(&obj_desc->method.owner_id); + walk_state->owner_id = obj_desc->method.owner_id; + + /* Push start scope on scope stack and make it current */ + + status = acpi_ds_scope_stack_push(method, method->type, walk_state); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Parse the entire method AML including deferred operators */ + + walk_state->parse_flags &= ~ACPI_PARSE_DELETE_TREE; + walk_state->parse_flags |= ACPI_PARSE_DISASSEMBLE; + + status = acpi_ps_parse_aml(walk_state); + +#ifdef ACPI_DISASSEMBLER + (void)acpi_dm_parse_deferred_ops(op); + + /* Now we can disassemble the method */ + + acpi_gbl_dm_opt_verbose = FALSE; + acpi_dm_disassemble(NULL, op, 0); + acpi_gbl_dm_opt_verbose = TRUE; +#endif + + acpi_ps_delete_parse_tree(op); + + /* Method cleanup */ + + acpi_ns_delete_namespace_subtree(method); + acpi_ns_delete_namespace_by_owner(obj_desc->method.owner_id); + acpi_ut_release_owner_id(&obj_desc->method.owner_id); + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbnames.c b/drivers/acpi/acpica/dbnames.c new file mode 100644 index 000000000000..04ff1ebfda58 --- /dev/null +++ b/drivers/acpi/acpica/dbnames.c @@ -0,0 +1,947 @@ +/******************************************************************************* + * + * Module Name: dbnames - Debugger commands for the acpi namespace + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdebug.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbnames") + +/* Local prototypes */ +static acpi_status +acpi_db_walk_and_match_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_predefined_names(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_specific_objects(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_object_counts(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_integrity_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_references(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_bus_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +/* + * Arguments for the Objects command + * These object types map directly to the ACPI_TYPES + */ +static struct acpi_db_argument_info acpi_db_object_types[] = { + {"ANY"}, + {"INTEGERS"}, + {"STRINGS"}, + {"BUFFERS"}, + {"PACKAGES"}, + {"FIELDS"}, + {"DEVICES"}, + {"EVENTS"}, + {"METHODS"}, + {"MUTEXES"}, + {"REGIONS"}, + {"POWERRESOURCES"}, + {"PROCESSORS"}, + {"THERMALZONES"}, + {"BUFFERFIELDS"}, + {"DDBHANDLES"}, + {"DEBUG"}, + {"REGIONFIELDS"}, + {"BANKFIELDS"}, + {"INDEXFIELDS"}, + {"REFERENCES"}, + {"ALIASES"}, + {"METHODALIASES"}, + {"NOTIFY"}, + {"ADDRESSHANDLER"}, + {"RESOURCE"}, + {"RESOURCEFIELD"}, + {"SCOPES"}, + {NULL} /* Must be null terminated */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_scope + * + * PARAMETERS: name - New scope path + * + * RETURN: Status + * + * DESCRIPTION: Set the "current scope" as maintained by this utility. + * The scope is used as a prefix to ACPI paths. + * + ******************************************************************************/ + +void acpi_db_set_scope(char *name) +{ + acpi_status status; + struct acpi_namespace_node *node; + + if (!name || name[0] == 0) { + acpi_os_printf("Current scope: %s\n", acpi_gbl_db_scope_buf); + return; + } + + acpi_db_prep_namestring(name); + + if (ACPI_IS_ROOT_PREFIX(name[0])) { + + /* Validate new scope from the root */ + + status = acpi_ns_get_node(acpi_gbl_root_node, name, + ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_gbl_db_scope_buf[0] = 0; + } else { + /* Validate new scope relative to old scope */ + + status = acpi_ns_get_node(acpi_gbl_db_scope_node, name, + ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + } + + /* Build the final pathname */ + + if (acpi_ut_safe_strcat + (acpi_gbl_db_scope_buf, sizeof(acpi_gbl_db_scope_buf), name)) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + + if (acpi_ut_safe_strcat + (acpi_gbl_db_scope_buf, sizeof(acpi_gbl_db_scope_buf), "\\")) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + + acpi_gbl_db_scope_node = node; + acpi_os_printf("New scope: %s\n", acpi_gbl_db_scope_buf); + return; + +error_exit: + + acpi_os_printf("Could not attach scope: %s, %s\n", + name, acpi_format_exception(status)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_namespace + * + * PARAMETERS: start_arg - Node to begin namespace dump + * depth_arg - Maximum tree depth to be dumped + * + * RETURN: None + * + * DESCRIPTION: Dump entire namespace or a subtree. Each node is displayed + * with type and other information. + * + ******************************************************************************/ + +void acpi_db_dump_namespace(char *start_arg, char *depth_arg) +{ + acpi_handle subtree_entry = acpi_gbl_root_node; + u32 max_depth = ACPI_UINT32_MAX; + + /* No argument given, just start at the root and dump entire namespace */ + + if (start_arg) { + subtree_entry = acpi_db_convert_to_node(start_arg); + if (!subtree_entry) { + return; + } + + /* Now we can check for the depth argument */ + + if (depth_arg) { + max_depth = strtoul(depth_arg, NULL, 0); + } + } + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("ACPI Namespace (from %4.4s (%p) subtree):\n", + ((struct acpi_namespace_node *)subtree_entry)->name. + ascii, subtree_entry); + + /* Display the subtree */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, max_depth, + ACPI_OWNER_ID_MAX, subtree_entry); + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_namespace_paths + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Dump entire namespace with full object pathnames and object + * type information. Alternative to "namespace" command. + * + ******************************************************************************/ + +void acpi_db_dump_namespace_paths(void) +{ + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("ACPI Namespace (from root):\n"); + + /* Display the entire namespace */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ns_dump_object_paths(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, + ACPI_UINT32_MAX, ACPI_OWNER_ID_MAX, + acpi_gbl_root_node); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_namespace_by_owner + * + * PARAMETERS: owner_arg - Owner ID whose nodes will be displayed + * depth_arg - Maximum tree depth to be dumped + * + * RETURN: None + * + * DESCRIPTION: Dump elements of the namespace that are owned by the owner_id. + * + ******************************************************************************/ + +void acpi_db_dump_namespace_by_owner(char *owner_arg, char *depth_arg) +{ + acpi_handle subtree_entry = acpi_gbl_root_node; + u32 max_depth = ACPI_UINT32_MAX; + acpi_owner_id owner_id; + + owner_id = (acpi_owner_id) strtoul(owner_arg, NULL, 0); + + /* Now we can check for the depth argument */ + + if (depth_arg) { + max_depth = strtoul(depth_arg, NULL, 0); + } + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("ACPI Namespace by owner %X:\n", owner_id); + + /* Display the subtree */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, max_depth, + owner_id, subtree_entry); + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_and_match_name + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Find a particular name/names within the namespace. Wildcards + * are supported -- '?' matches any character. + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_and_match_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + acpi_status status; + char *requested_name = (char *)context; + u32 i; + struct acpi_buffer buffer; + struct acpi_walk_info info; + + /* Check for a name match */ + + for (i = 0; i < 4; i++) { + + /* Wildcard support */ + + if ((requested_name[i] != '?') && + (requested_name[i] != ((struct acpi_namespace_node *) + obj_handle)->name.ascii[i])) { + + /* No match, just exit */ + + return (AE_OK); + } + } + + /* Get the full pathname to this object */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could Not get pathname for object %p\n", + obj_handle); + } else { + info.owner_id = ACPI_OWNER_ID_MAX; + info.debug_level = ACPI_UINT32_MAX; + info.display_type = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT; + + acpi_os_printf("%32s", (char *)buffer.pointer); + (void)acpi_ns_dump_one_object(obj_handle, nesting_level, &info, + NULL); + ACPI_FREE(buffer.pointer); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_find_name_in_namespace + * + * PARAMETERS: name_arg - The 4-character ACPI name to find. + * wildcards are supported. + * + * RETURN: None + * + * DESCRIPTION: Search the namespace for a given name (with wildcards) + * + ******************************************************************************/ + +acpi_status acpi_db_find_name_in_namespace(char *name_arg) +{ + char acpi_name[5] = "____"; + char *acpi_name_ptr = acpi_name; + + if (strlen(name_arg) > ACPI_NAME_SIZE) { + acpi_os_printf("Name must be no longer than 4 characters\n"); + return (AE_OK); + } + + /* Pad out name with underscores as necessary to create a 4-char name */ + + acpi_ut_strupr(name_arg); + while (*name_arg) { + *acpi_name_ptr = *name_arg; + acpi_name_ptr++; + name_arg++; + } + + /* Walk the namespace from the root */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_walk_and_match_name, + NULL, acpi_name, NULL); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_predefined_names + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Detect and display predefined ACPI names (names that start with + * an underscore) + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_predefined_names(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + u32 *count = (u32 *)context; + const union acpi_predefined_info *predefined; + const union acpi_predefined_info *package = NULL; + char *pathname; + char string_buffer[48]; + + predefined = acpi_ut_match_predefined_method(node->name.ascii); + if (!predefined) { + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + if (!pathname) { + return (AE_OK); + } + + /* If method returns a package, the info is in the next table entry */ + + if (predefined->info.expected_btypes & ACPI_RTYPE_PACKAGE) { + package = predefined + 1; + } + + acpi_ut_get_expected_return_types(string_buffer, + predefined->info.expected_btypes); + + acpi_os_printf("%-32s Arguments %X, Return Types: %s", pathname, + METHOD_GET_ARG_COUNT(predefined->info.argument_list), + string_buffer); + + if (package) { + acpi_os_printf(" (PkgType %2.2X, ObjType %2.2X, Count %2.2X)", + package->ret_info.type, + package->ret_info.object_type1, + package->ret_info.count1); + } + + acpi_os_printf("\n"); + + /* Check that the declared argument count matches the ACPI spec */ + + acpi_ns_check_acpi_compliance(pathname, node, predefined); + + ACPI_FREE(pathname); + (*count)++; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_check_predefined_names + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Validate all predefined names in the namespace + * + ******************************************************************************/ + +void acpi_db_check_predefined_names(void) +{ + u32 count = 0; + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_walk_for_predefined_names, NULL, + (void *)&count, NULL); + + acpi_os_printf("Found %u predefined names in the namespace\n", count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_object_counts + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Display short info about objects in the namespace + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_object_counts(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_object_info *info = (struct acpi_object_info *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + + if (node->type > ACPI_TYPE_NS_NODE_MAX) { + acpi_os_printf("[%4.4s]: Unknown object type %X\n", + node->name.ascii, node->type); + } else { + info->types[node->type]++; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_specific_objects + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Display short info about objects in the namespace + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_specific_objects(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_walk_info *info = (struct acpi_walk_info *)context; + struct acpi_buffer buffer; + acpi_status status; + + info->count++; + + /* Get and display the full pathname to this object */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could Not get pathname for object %p\n", + obj_handle); + return (AE_OK); + } + + acpi_os_printf("%32s", (char *)buffer.pointer); + ACPI_FREE(buffer.pointer); + + /* Dump short info about the object */ + + (void)acpi_ns_dump_one_object(obj_handle, nesting_level, info, NULL); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_objects + * + * PARAMETERS: obj_type_arg - Type of object to display + * display_count_arg - Max depth to display + * + * RETURN: None + * + * DESCRIPTION: Display objects in the namespace of the requested type + * + ******************************************************************************/ + +acpi_status acpi_db_display_objects(char *obj_type_arg, char *display_count_arg) +{ + struct acpi_walk_info info; + acpi_object_type type; + struct acpi_object_info *object_info; + u32 i; + u32 total_objects = 0; + + /* No argument means display summary/count of all object types */ + + if (!obj_type_arg) { + object_info = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_object_info)); + + /* Walk the namespace from the root */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_walk_for_object_counts, NULL, + (void *)object_info, NULL); + + acpi_os_printf("\nSummary of namespace objects:\n\n"); + + for (i = 0; i < ACPI_TOTAL_TYPES; i++) { + acpi_os_printf("%8u %s\n", object_info->types[i], + acpi_ut_get_type_name(i)); + + total_objects += object_info->types[i]; + } + + acpi_os_printf("\n%8u Total namespace objects\n\n", + total_objects); + + ACPI_FREE(object_info); + return (AE_OK); + } + + /* Get the object type */ + + type = acpi_db_match_argument(obj_type_arg, acpi_db_object_types); + if (type == ACPI_TYPE_NOT_FOUND) { + acpi_os_printf("Invalid or unsupported argument\n"); + return (AE_OK); + } + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf + ("Objects of type [%s] defined in the current ACPI Namespace:\n", + acpi_ut_get_type_name(type)); + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + + info.count = 0; + info.owner_id = ACPI_OWNER_ID_MAX; + info.debug_level = ACPI_UINT32_MAX; + info.display_type = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT; + + /* Walk the namespace from the root */ + + (void)acpi_walk_namespace(type, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + acpi_db_walk_for_specific_objects, NULL, + (void *)&info, NULL); + + acpi_os_printf + ("\nFound %u objects of type [%s] in the current ACPI Namespace\n", + info.count, acpi_ut_get_type_name(type)); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_integrity_walk + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Examine one NS node for valid values. + * + ******************************************************************************/ + +static acpi_status +acpi_db_integrity_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_integrity_info *info = + (struct acpi_integrity_info *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + union acpi_operand_object *object; + u8 alias = TRUE; + + info->nodes++; + + /* Verify the NS node, and dereference aliases */ + + while (alias) { + if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { + acpi_os_printf + ("Invalid Descriptor Type for Node %p [%s] - " + "is %2.2X should be %2.2X\n", node, + acpi_ut_get_descriptor_name(node), + ACPI_GET_DESCRIPTOR_TYPE(node), + ACPI_DESC_TYPE_NAMED); + return (AE_OK); + } + + if ((node->type == ACPI_TYPE_LOCAL_ALIAS) || + (node->type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) { + node = (struct acpi_namespace_node *)node->object; + } else { + alias = FALSE; + } + } + + if (node->type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf("Invalid Object Type for Node %p, Type = %X\n", + node, node->type); + return (AE_OK); + } + + if (!acpi_ut_valid_acpi_name(node->name.ascii)) { + acpi_os_printf("Invalid AcpiName for Node %p\n", node); + return (AE_OK); + } + + object = acpi_ns_get_attached_object(node); + if (object) { + info->objects++; + if (ACPI_GET_DESCRIPTOR_TYPE(object) != ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf + ("Invalid Descriptor Type for Object %p [%s]\n", + object, acpi_ut_get_descriptor_name(object)); + } + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_check_integrity + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Check entire namespace for data structure integrity + * + ******************************************************************************/ + +void acpi_db_check_integrity(void) +{ + struct acpi_integrity_info info = { 0, 0 }; + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_integrity_walk, NULL, + (void *)&info, NULL); + + acpi_os_printf("Verified %u namespace nodes with %u Objects\n", + info.nodes, info.objects); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_references + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Check if this namespace object refers to the target object + * that is passed in as the context value. + * + * Note: Currently doesn't check subobjects within the Node's object + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_references(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + union acpi_operand_object *obj_desc = + (union acpi_operand_object *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + + /* Check for match against the namespace node itself */ + + if (node == (void *)obj_desc) { + acpi_os_printf("Object is a Node [%4.4s]\n", + acpi_ut_get_node_name(node)); + } + + /* Check for match against the object attached to the node */ + + if (acpi_ns_get_attached_object(node) == obj_desc) { + acpi_os_printf("Reference at Node->Object %p [%4.4s]\n", + node, acpi_ut_get_node_name(node)); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_find_references + * + * PARAMETERS: object_arg - String with hex value of the object + * + * RETURN: None + * + * DESCRIPTION: Search namespace for all references to the input object + * + ******************************************************************************/ + +void acpi_db_find_references(char *object_arg) +{ + union acpi_operand_object *obj_desc; + acpi_size address; + + /* Convert string to object pointer */ + + address = strtoul(object_arg, NULL, 16); + obj_desc = ACPI_TO_POINTER(address); + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_walk_for_references, + NULL, (void *)obj_desc, NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_bus_walk + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Display info about device objects that have a corresponding + * _PRT method. + * + ******************************************************************************/ + +static acpi_status +acpi_db_bus_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + acpi_status status; + struct acpi_buffer buffer; + struct acpi_namespace_node *temp_node; + struct acpi_device_info *info; + u32 i; + + if ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_PROCESSOR)) { + return (AE_OK); + } + + /* Exit if there is no _PRT under this device */ + + status = acpi_get_handle(node, METHOD_NAME__PRT, + ACPI_CAST_PTR(acpi_handle, &temp_node)); + if (ACPI_FAILURE(status)) { + return (AE_OK); + } + + /* Get the full path to this device object */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could Not get pathname for object %p\n", + obj_handle); + return (AE_OK); + } + + status = acpi_get_object_info(obj_handle, &info); + if (ACPI_FAILURE(status)) { + return (AE_OK); + } + + /* Display the full path */ + + acpi_os_printf("%-32s Type %X", (char *)buffer.pointer, node->type); + ACPI_FREE(buffer.pointer); + + if (info->flags & ACPI_PCI_ROOT_BRIDGE) { + acpi_os_printf(" - Is PCI Root Bridge"); + } + acpi_os_printf("\n"); + + /* _PRT info */ + + acpi_os_printf("_PRT: %p\n", temp_node); + + /* Dump _ADR, _HID, _UID, _CID */ + + if (info->valid & ACPI_VALID_ADR) { + acpi_os_printf("_ADR: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(info->address)); + } else { + acpi_os_printf("_ADR: \n"); + } + + if (info->valid & ACPI_VALID_HID) { + acpi_os_printf("_HID: %s\n", info->hardware_id.string); + } else { + acpi_os_printf("_HID: \n"); + } + + if (info->valid & ACPI_VALID_UID) { + acpi_os_printf("_UID: %s\n", info->unique_id.string); + } else { + acpi_os_printf("_UID: \n"); + } + + if (info->valid & ACPI_VALID_CID) { + for (i = 0; i < info->compatible_id_list.count; i++) { + acpi_os_printf("_CID: %s\n", + info->compatible_id_list.ids[i].string); + } + } else { + acpi_os_printf("_CID: \n"); + } + + ACPI_FREE(info); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_bus_info + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display info about system busses. + * + ******************************************************************************/ + +void acpi_db_get_bus_info(void) +{ + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_bus_walk, NULL, NULL, + NULL); +} diff --git a/drivers/acpi/acpica/dbobject.c b/drivers/acpi/acpica/dbobject.c new file mode 100644 index 000000000000..116f6db8c2ed --- /dev/null +++ b/drivers/acpi/acpica/dbobject.c @@ -0,0 +1,533 @@ +/******************************************************************************* + * + * Module Name: dbobject - ACPI object decode and display + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbobject") + +/* Local prototypes */ +static void acpi_db_decode_node(struct acpi_namespace_node *node); + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_method_info + * + * PARAMETERS: status - Method execution status + * walk_state - Current state of the parse tree walk + * + * RETURN: None + * + * DESCRIPTION: Called when a method has been aborted because of an error. + * Dumps the method execution stack, and the method locals/args, + * and disassembles the AML opcode that failed. + * + ******************************************************************************/ + +void +acpi_db_dump_method_info(acpi_status status, struct acpi_walk_state *walk_state) +{ + struct acpi_thread_state *thread; + + /* Ignore control codes, they are not errors */ + + if ((status & AE_CODE_MASK) == AE_CODE_CONTROL) { + return; + } + + /* We may be executing a deferred opcode */ + + if (walk_state->deferred_node) { + acpi_os_printf("Executing subtree for Buffer/Package/Region\n"); + return; + } + + /* + * If there is no Thread, we are not actually executing a method. + * This can happen when the iASL compiler calls the interpreter + * to perform constant folding. + */ + thread = walk_state->thread; + if (!thread) { + return; + } + + /* Display the method locals and arguments */ + + acpi_os_printf("\n"); + acpi_db_decode_locals(walk_state); + acpi_os_printf("\n"); + acpi_db_decode_arguments(walk_state); + acpi_os_printf("\n"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_internal_object + * + * PARAMETERS: obj_desc - Object to be displayed + * + * RETURN: None + * + * DESCRIPTION: Short display of an internal object. Numbers/Strings/Buffers. + * + ******************************************************************************/ + +void acpi_db_decode_internal_object(union acpi_operand_object *obj_desc) +{ + u32 i; + + if (!obj_desc) { + acpi_os_printf(" Uninitialized"); + return; + } + + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf(" %p [%s]", obj_desc, + acpi_ut_get_descriptor_name(obj_desc)); + return; + } + + acpi_os_printf(" %s", acpi_ut_get_object_type_name(obj_desc)); + + switch (obj_desc->common.type) { + case ACPI_TYPE_INTEGER: + + acpi_os_printf(" %8.8X%8.8X", + ACPI_FORMAT_UINT64(obj_desc->integer.value)); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("(%u) \"%.24s", + obj_desc->string.length, + obj_desc->string.pointer); + + if (obj_desc->string.length > 24) { + acpi_os_printf("..."); + } else { + acpi_os_printf("\""); + } + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("(%u)", obj_desc->buffer.length); + for (i = 0; (i < 8) && (i < obj_desc->buffer.length); i++) { + acpi_os_printf(" %2.2X", obj_desc->buffer.pointer[i]); + } + break; + + default: + + acpi_os_printf(" %p", obj_desc); + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_node + * + * PARAMETERS: node - Object to be displayed + * + * RETURN: None + * + * DESCRIPTION: Short display of a namespace node + * + ******************************************************************************/ + +static void acpi_db_decode_node(struct acpi_namespace_node *node) +{ + + acpi_os_printf(" Name %4.4s", + acpi_ut_get_node_name(node)); + + if (node->flags & ANOBJ_METHOD_ARG) { + acpi_os_printf(" [Method Arg]"); + } + if (node->flags & ANOBJ_METHOD_LOCAL) { + acpi_os_printf(" [Method Local]"); + } + + switch (node->type) { + + /* These types have no attached object */ + + case ACPI_TYPE_DEVICE: + + acpi_os_printf(" Device"); + break; + + case ACPI_TYPE_THERMAL: + + acpi_os_printf(" Thermal Zone"); + break; + + default: + + acpi_db_decode_internal_object(acpi_ns_get_attached_object + (node)); + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_internal_object + * + * PARAMETERS: obj_desc - Object to be displayed + * walk_state - Current walk state + * + * RETURN: None + * + * DESCRIPTION: Short display of an internal object + * + ******************************************************************************/ + +void +acpi_db_display_internal_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + u8 type; + + acpi_os_printf("%p ", obj_desc); + + if (!obj_desc) { + acpi_os_printf("\n"); + return; + } + + /* Decode the object type */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { + case ACPI_DESC_TYPE_PARSER: + + acpi_os_printf(" "); + break; + + case ACPI_DESC_TYPE_NAMED: + + acpi_db_decode_node((struct acpi_namespace_node *)obj_desc); + break; + + case ACPI_DESC_TYPE_OPERAND: + + type = obj_desc->common.type; + if (type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf(" Type %X [Invalid Type]", (u32)type); + return; + } + + /* Decode the ACPI object type */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[%s] ", + acpi_ut_get_reference_name(obj_desc)); + + /* Decode the refererence */ + + switch (obj_desc->reference.class) { + case ACPI_REFCLASS_LOCAL: + + acpi_os_printf("%X ", + obj_desc->reference.value); + if (walk_state) { + obj_desc = walk_state->local_variables + [obj_desc->reference.value].object; + acpi_os_printf("%p", obj_desc); + acpi_db_decode_internal_object + (obj_desc); + } + break; + + case ACPI_REFCLASS_ARG: + + acpi_os_printf("%X ", + obj_desc->reference.value); + if (walk_state) { + obj_desc = walk_state->arguments + [obj_desc->reference.value].object; + acpi_os_printf("%p", obj_desc); + acpi_db_decode_internal_object + (obj_desc); + } + break; + + case ACPI_REFCLASS_INDEX: + + switch (obj_desc->reference.target_type) { + case ACPI_TYPE_BUFFER_FIELD: + + acpi_os_printf("%p", + obj_desc->reference. + object); + acpi_db_decode_internal_object + (obj_desc->reference.object); + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("%p", + obj_desc->reference. + where); + if (!obj_desc->reference.where) { + acpi_os_printf + (" Uninitialized WHERE pointer"); + } else { + acpi_db_decode_internal_object(* + (obj_desc-> + reference. + where)); + } + break; + + default: + + acpi_os_printf + ("Unknown index target type"); + break; + } + break; + + case ACPI_REFCLASS_REFOF: + + if (!obj_desc->reference.object) { + acpi_os_printf + ("Uninitialized reference subobject pointer"); + break; + } + + /* Reference can be to a Node or an Operand object */ + + switch (ACPI_GET_DESCRIPTOR_TYPE + (obj_desc->reference.object)) { + case ACPI_DESC_TYPE_NAMED: + + acpi_db_decode_node(obj_desc->reference. + object); + break; + + case ACPI_DESC_TYPE_OPERAND: + + acpi_db_decode_internal_object + (obj_desc->reference.object); + break; + + default: + break; + } + break; + + case ACPI_REFCLASS_NAME: + + acpi_db_decode_node(obj_desc->reference.node); + break; + + case ACPI_REFCLASS_DEBUG: + case ACPI_REFCLASS_TABLE: + + acpi_os_printf("\n"); + break; + + default: /* Unknown reference class */ + + acpi_os_printf("%2.2X\n", + obj_desc->reference.class); + break; + } + break; + + default: + + acpi_os_printf(" "); + acpi_db_decode_internal_object(obj_desc); + break; + } + break; + + default: + + acpi_os_printf(" [%s]", + acpi_ut_get_descriptor_name(obj_desc)); + break; + } + + acpi_os_printf("\n"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_locals + * + * PARAMETERS: walk_state - State for current method + * + * RETURN: None + * + * DESCRIPTION: Display all locals for the currently running control method + * + ******************************************************************************/ + +void acpi_db_decode_locals(struct acpi_walk_state *walk_state) +{ + u32 i; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + u8 display_locals = FALSE; + + obj_desc = walk_state->method_desc; + node = walk_state->method_node; + + if (!node) { + acpi_os_printf + ("No method node (Executing subtree for buffer or opregion)\n"); + return; + } + + if (node->type != ACPI_TYPE_METHOD) { + acpi_os_printf("Executing subtree for Buffer/Package/Region\n"); + return; + } + + /* Are any locals actually set? */ + + for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) { + obj_desc = walk_state->local_variables[i].object; + if (obj_desc) { + display_locals = TRUE; + break; + } + } + + /* If any are set, only display the ones that are set */ + + if (display_locals) { + acpi_os_printf + ("\nInitialized Local Variables for method [%4.4s]:\n", + acpi_ut_get_node_name(node)); + + for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) { + obj_desc = walk_state->local_variables[i].object; + if (obj_desc) { + acpi_os_printf(" Local%X: ", i); + acpi_db_display_internal_object(obj_desc, + walk_state); + } + } + } else { + acpi_os_printf + ("No Local Variables are initialized for method [%4.4s]\n", + acpi_ut_get_node_name(node)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_arguments + * + * PARAMETERS: walk_state - State for current method + * + * RETURN: None + * + * DESCRIPTION: Display all arguments for the currently running control method + * + ******************************************************************************/ + +void acpi_db_decode_arguments(struct acpi_walk_state *walk_state) +{ + u32 i; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + u8 display_args = FALSE; + + node = walk_state->method_node; + obj_desc = walk_state->method_desc; + + if (!node) { + acpi_os_printf + ("No method node (Executing subtree for buffer or opregion)\n"); + return; + } + + if (node->type != ACPI_TYPE_METHOD) { + acpi_os_printf("Executing subtree for Buffer/Package/Region\n"); + return; + } + + /* Are any arguments actually set? */ + + for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) { + obj_desc = walk_state->arguments[i].object; + if (obj_desc) { + display_args = TRUE; + break; + } + } + + /* If any are set, only display the ones that are set */ + + if (display_args) { + acpi_os_printf("Initialized Arguments for Method [%4.4s]: " + "(%X arguments defined for method invocation)\n", + acpi_ut_get_node_name(node), + obj_desc->method.param_count); + + for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) { + obj_desc = walk_state->arguments[i].object; + if (obj_desc) { + acpi_os_printf(" Arg%u: ", i); + acpi_db_display_internal_object(obj_desc, + walk_state); + } + } + } else { + acpi_os_printf + ("No Arguments are initialized for method [%4.4s]\n", + acpi_ut_get_node_name(node)); + } +} diff --git a/drivers/acpi/acpica/dbstats.c b/drivers/acpi/acpica/dbstats.c new file mode 100644 index 000000000000..4ba0a20811eb --- /dev/null +++ b/drivers/acpi/acpica/dbstats.c @@ -0,0 +1,546 @@ +/******************************************************************************* + * + * Module Name: dbstats - Generation and display of ACPI table statistics + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbstats") + +/* Local prototypes */ +static void acpi_db_count_namespace_objects(void); + +static void acpi_db_enumerate_object(union acpi_operand_object *obj_desc); + +static acpi_status +acpi_db_classify_one_object(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE +static void acpi_db_list_info(struct acpi_memory_list *list); +#endif + +/* + * Statistics subcommands + */ +static struct acpi_db_argument_info acpi_db_stat_types[] = { + {"ALLOCATIONS"}, + {"OBJECTS"}, + {"MEMORY"}, + {"MISC"}, + {"TABLES"}, + {"SIZES"}, + {"STACK"}, + {NULL} /* Must be null terminated */ +}; + +#define CMD_STAT_ALLOCATIONS 0 +#define CMD_STAT_OBJECTS 1 +#define CMD_STAT_MEMORY 2 +#define CMD_STAT_MISC 3 +#define CMD_STAT_TABLES 4 +#define CMD_STAT_SIZES 5 +#define CMD_STAT_STACK 6 + +#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE +/******************************************************************************* + * + * FUNCTION: acpi_db_list_info + * + * PARAMETERS: list - Memory list/cache to be displayed + * + * RETURN: None + * + * DESCRIPTION: Display information about the input memory list or cache. + * + ******************************************************************************/ + +static void acpi_db_list_info(struct acpi_memory_list *list) +{ +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + u32 outstanding; +#endif + + acpi_os_printf("\n%s\n", list->list_name); + + /* max_depth > 0 indicates a cache object */ + + if (list->max_depth > 0) { + acpi_os_printf + (" Cache: [Depth MaxD Avail Size] " + "%8.2X %8.2X %8.2X %8.2X\n", list->current_depth, + list->max_depth, list->max_depth - list->current_depth, + (list->current_depth * list->object_size)); + } +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + if (list->max_depth > 0) { + acpi_os_printf + (" Cache: [Requests Hits Misses ObjSize] " + "%8.2X %8.2X %8.2X %8.2X\n", list->requests, list->hits, + list->requests - list->hits, list->object_size); + } + + outstanding = acpi_db_get_cache_info(list); + + if (list->object_size) { + acpi_os_printf + (" Mem: [Alloc Free Max CurSize Outstanding] " + "%8.2X %8.2X %8.2X %8.2X %8.2X\n", list->total_allocated, + list->total_freed, list->max_occupied, + outstanding * list->object_size, outstanding); + } else { + acpi_os_printf + (" Mem: [Alloc Free Max CurSize Outstanding Total] " + "%8.2X %8.2X %8.2X %8.2X %8.2X %8.2X\n", + list->total_allocated, list->total_freed, + list->max_occupied, list->current_total_size, outstanding, + list->total_size); + } +#endif +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_enumerate_object + * + * PARAMETERS: obj_desc - Object to be counted + * + * RETURN: None + * + * DESCRIPTION: Add this object to the global counts, by object type. + * Limited recursion handles subobjects and packages, and this + * is probably acceptable within the AML debugger only. + * + ******************************************************************************/ + +static void acpi_db_enumerate_object(union acpi_operand_object *obj_desc) +{ + u32 i; + + if (!obj_desc) { + return; + } + + /* Enumerate this object first */ + + acpi_gbl_num_objects++; + + if (obj_desc->common.type > ACPI_TYPE_NS_NODE_MAX) { + acpi_gbl_obj_type_count_misc++; + } else { + acpi_gbl_obj_type_count[obj_desc->common.type]++; + } + + /* Count the sub-objects */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_PACKAGE: + + for (i = 0; i < obj_desc->package.count; i++) { + acpi_db_enumerate_object(obj_desc->package.elements[i]); + } + break; + + case ACPI_TYPE_DEVICE: + + acpi_db_enumerate_object(obj_desc->device.notify_list[0]); + acpi_db_enumerate_object(obj_desc->device.notify_list[1]); + acpi_db_enumerate_object(obj_desc->device.handler); + break; + + case ACPI_TYPE_BUFFER_FIELD: + + if (acpi_ns_get_secondary_object(obj_desc)) { + acpi_gbl_obj_type_count[ACPI_TYPE_BUFFER_FIELD]++; + } + break; + + case ACPI_TYPE_REGION: + + acpi_gbl_obj_type_count[ACPI_TYPE_LOCAL_REGION_FIELD]++; + acpi_db_enumerate_object(obj_desc->region.handler); + break; + + case ACPI_TYPE_POWER: + + acpi_db_enumerate_object(obj_desc->power_resource. + notify_list[0]); + acpi_db_enumerate_object(obj_desc->power_resource. + notify_list[1]); + break; + + case ACPI_TYPE_PROCESSOR: + + acpi_db_enumerate_object(obj_desc->processor.notify_list[0]); + acpi_db_enumerate_object(obj_desc->processor.notify_list[1]); + acpi_db_enumerate_object(obj_desc->processor.handler); + break; + + case ACPI_TYPE_THERMAL: + + acpi_db_enumerate_object(obj_desc->thermal_zone.notify_list[0]); + acpi_db_enumerate_object(obj_desc->thermal_zone.notify_list[1]); + acpi_db_enumerate_object(obj_desc->thermal_zone.handler); + break; + + default: + + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_classify_one_object + * + * PARAMETERS: Callback for walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Enumerate both the object descriptor (including subobjects) and + * the parent namespace node. + * + ******************************************************************************/ + +static acpi_status +acpi_db_classify_one_object(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node; + union acpi_operand_object *obj_desc; + u32 type; + + acpi_gbl_num_nodes++; + + node = (struct acpi_namespace_node *)obj_handle; + obj_desc = acpi_ns_get_attached_object(node); + + acpi_db_enumerate_object(obj_desc); + + type = node->type; + if (type > ACPI_TYPE_NS_NODE_MAX) { + acpi_gbl_node_type_count_misc++; + } else { + acpi_gbl_node_type_count[type]++; + } + + return (AE_OK); + +#ifdef ACPI_FUTURE_IMPLEMENTATION + + /* TBD: These need to be counted during the initial parsing phase */ + + if (acpi_ps_is_named_op(op->opcode)) { + num_nodes++; + } + + if (is_method) { + num_method_elements++; + } + + num_grammar_elements++; + op = acpi_ps_get_depth_next(root, op); + + size_of_parse_tree = (num_grammar_elements - num_method_elements) * + (u32)sizeof(union acpi_parse_object); + size_of_method_trees = + num_method_elements * (u32)sizeof(union acpi_parse_object); + size_of_node_entries = + num_nodes * (u32)sizeof(struct acpi_namespace_node); + size_of_acpi_objects = + num_nodes * (u32)sizeof(union acpi_operand_object); +#endif +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_count_namespace_objects + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Count and classify the entire namespace, including all + * namespace nodes and attached objects. + * + ******************************************************************************/ + +static void acpi_db_count_namespace_objects(void) +{ + u32 i; + + acpi_gbl_num_nodes = 0; + acpi_gbl_num_objects = 0; + + acpi_gbl_obj_type_count_misc = 0; + for (i = 0; i < (ACPI_TYPE_NS_NODE_MAX - 1); i++) { + acpi_gbl_obj_type_count[i] = 0; + acpi_gbl_node_type_count[i] = 0; + } + + (void)acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, FALSE, + acpi_db_classify_one_object, NULL, NULL, + NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_statistics + * + * PARAMETERS: type_arg - Subcommand + * + * RETURN: Status + * + * DESCRIPTION: Display various statistics + * + ******************************************************************************/ + +acpi_status acpi_db_display_statistics(char *type_arg) +{ + u32 i; + u32 temp; + + acpi_ut_strupr(type_arg); + temp = acpi_db_match_argument(type_arg, acpi_db_stat_types); + if (temp == ACPI_TYPE_NOT_FOUND) { + acpi_os_printf("Invalid or unsupported argument\n"); + return (AE_OK); + } + + switch (temp) { + case CMD_STAT_ALLOCATIONS: + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_ut_dump_allocation_info(); +#endif + break; + + case CMD_STAT_TABLES: + + acpi_os_printf("ACPI Table Information (not implemented):\n\n"); + break; + + case CMD_STAT_OBJECTS: + + acpi_db_count_namespace_objects(); + + acpi_os_printf + ("\nObjects defined in the current namespace:\n\n"); + + acpi_os_printf("%16.16s %10.10s %10.10s\n", + "ACPI_TYPE", "NODES", "OBJECTS"); + + for (i = 0; i < ACPI_TYPE_NS_NODE_MAX; i++) { + acpi_os_printf("%16.16s % 10ld% 10ld\n", + acpi_ut_get_type_name(i), + acpi_gbl_node_type_count[i], + acpi_gbl_obj_type_count[i]); + } + acpi_os_printf("%16.16s % 10ld% 10ld\n", "Misc/Unknown", + acpi_gbl_node_type_count_misc, + acpi_gbl_obj_type_count_misc); + + acpi_os_printf("%16.16s % 10ld% 10ld\n", "TOTALS:", + acpi_gbl_num_nodes, acpi_gbl_num_objects); + break; + + case CMD_STAT_MEMORY: + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_os_printf + ("\n----Object Statistics (all in hex)---------\n"); + + acpi_db_list_info(acpi_gbl_global_list); + acpi_db_list_info(acpi_gbl_ns_node_list); +#endif + +#ifdef ACPI_USE_LOCAL_CACHE + acpi_os_printf + ("\n----Cache Statistics (all in hex)---------\n"); + acpi_db_list_info(acpi_gbl_operand_cache); + acpi_db_list_info(acpi_gbl_ps_node_cache); + acpi_db_list_info(acpi_gbl_ps_node_ext_cache); + acpi_db_list_info(acpi_gbl_state_cache); +#endif + + break; + + case CMD_STAT_MISC: + + acpi_os_printf("\nMiscellaneous Statistics:\n\n"); + acpi_os_printf("Calls to AcpiPsFind:.. ........% 7ld\n", + acpi_gbl_ps_find_count); + acpi_os_printf("Calls to AcpiNsLookup:..........% 7ld\n", + acpi_gbl_ns_lookup_count); + + acpi_os_printf("\n"); + + acpi_os_printf("Mutex usage:\n\n"); + for (i = 0; i < ACPI_NUM_MUTEX; i++) { + acpi_os_printf("%-28s: % 7ld\n", + acpi_ut_get_mutex_name(i), + acpi_gbl_mutex_info[i].use_count); + } + break; + + case CMD_STAT_SIZES: + + acpi_os_printf("\nInternal object sizes:\n\n"); + + acpi_os_printf("Common %3d\n", + sizeof(struct acpi_object_common)); + acpi_os_printf("Number %3d\n", + sizeof(struct acpi_object_integer)); + acpi_os_printf("String %3d\n", + sizeof(struct acpi_object_string)); + acpi_os_printf("Buffer %3d\n", + sizeof(struct acpi_object_buffer)); + acpi_os_printf("Package %3d\n", + sizeof(struct acpi_object_package)); + acpi_os_printf("BufferField %3d\n", + sizeof(struct acpi_object_buffer_field)); + acpi_os_printf("Device %3d\n", + sizeof(struct acpi_object_device)); + acpi_os_printf("Event %3d\n", + sizeof(struct acpi_object_event)); + acpi_os_printf("Method %3d\n", + sizeof(struct acpi_object_method)); + acpi_os_printf("Mutex %3d\n", + sizeof(struct acpi_object_mutex)); + acpi_os_printf("Region %3d\n", + sizeof(struct acpi_object_region)); + acpi_os_printf("PowerResource %3d\n", + sizeof(struct acpi_object_power_resource)); + acpi_os_printf("Processor %3d\n", + sizeof(struct acpi_object_processor)); + acpi_os_printf("ThermalZone %3d\n", + sizeof(struct acpi_object_thermal_zone)); + acpi_os_printf("RegionField %3d\n", + sizeof(struct acpi_object_region_field)); + acpi_os_printf("BankField %3d\n", + sizeof(struct acpi_object_bank_field)); + acpi_os_printf("IndexField %3d\n", + sizeof(struct acpi_object_index_field)); + acpi_os_printf("Reference %3d\n", + sizeof(struct acpi_object_reference)); + acpi_os_printf("Notify %3d\n", + sizeof(struct acpi_object_notify_handler)); + acpi_os_printf("AddressSpace %3d\n", + sizeof(struct acpi_object_addr_handler)); + acpi_os_printf("Extra %3d\n", + sizeof(struct acpi_object_extra)); + acpi_os_printf("Data %3d\n", + sizeof(struct acpi_object_data)); + + acpi_os_printf("\n"); + + acpi_os_printf("ParseObject %3d\n", + sizeof(struct acpi_parse_obj_common)); + acpi_os_printf("ParseObjectNamed %3d\n", + sizeof(struct acpi_parse_obj_named)); + acpi_os_printf("ParseObjectAsl %3d\n", + sizeof(struct acpi_parse_obj_asl)); + acpi_os_printf("OperandObject %3d\n", + sizeof(union acpi_operand_object)); + acpi_os_printf("NamespaceNode %3d\n", + sizeof(struct acpi_namespace_node)); + acpi_os_printf("AcpiObject %3d\n", + sizeof(union acpi_object)); + + acpi_os_printf("\n"); + + acpi_os_printf("Generic State %3d\n", + sizeof(union acpi_generic_state)); + acpi_os_printf("Common State %3d\n", + sizeof(struct acpi_common_state)); + acpi_os_printf("Control State %3d\n", + sizeof(struct acpi_control_state)); + acpi_os_printf("Update State %3d\n", + sizeof(struct acpi_update_state)); + acpi_os_printf("Scope State %3d\n", + sizeof(struct acpi_scope_state)); + acpi_os_printf("Parse Scope %3d\n", + sizeof(struct acpi_pscope_state)); + acpi_os_printf("Package State %3d\n", + sizeof(struct acpi_pkg_state)); + acpi_os_printf("Thread State %3d\n", + sizeof(struct acpi_thread_state)); + acpi_os_printf("Result Values %3d\n", + sizeof(struct acpi_result_values)); + acpi_os_printf("Notify Info %3d\n", + sizeof(struct acpi_notify_info)); + break; + + case CMD_STAT_STACK: +#if defined(ACPI_DEBUG_OUTPUT) + + temp = + (u32)ACPI_PTR_DIFF(acpi_gbl_entry_stack_pointer, + acpi_gbl_lowest_stack_pointer); + + acpi_os_printf("\nSubsystem Stack Usage:\n\n"); + acpi_os_printf("Entry Stack Pointer %p\n", + acpi_gbl_entry_stack_pointer); + acpi_os_printf("Lowest Stack Pointer %p\n", + acpi_gbl_lowest_stack_pointer); + acpi_os_printf("Stack Use %X (%u)\n", temp, + temp); + acpi_os_printf("Deepest Procedure Nesting %u\n", + acpi_gbl_deepest_nesting); +#endif + break; + + default: + + break; + } + + acpi_os_printf("\n"); + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbtest.c b/drivers/acpi/acpica/dbtest.c new file mode 100644 index 000000000000..10ea8bf9b810 --- /dev/null +++ b/drivers/acpi/acpica/dbtest.c @@ -0,0 +1,1057 @@ +/******************************************************************************* + * + * Module Name: dbtest - Various debug-related tests + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbtest") + +/* Local prototypes */ +static void acpi_db_test_all_objects(void); + +static acpi_status +acpi_db_test_one_object(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +static acpi_status +acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length); + +static acpi_status +acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length); + +static acpi_status +acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length); + +static acpi_status +acpi_db_read_from_object(struct acpi_namespace_node *node, + acpi_object_type expected_type, + union acpi_object **value); + +static acpi_status +acpi_db_write_to_object(struct acpi_namespace_node *node, + union acpi_object *value); + +static void acpi_db_evaluate_all_predefined_names(char *count_arg); + +static acpi_status +acpi_db_evaluate_one_predefined_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +/* + * Test subcommands + */ +static struct acpi_db_argument_info acpi_db_test_types[] = { + {"OBJECTS"}, + {"PREDEFINED"}, + {NULL} /* Must be null terminated */ +}; + +#define CMD_TEST_OBJECTS 0 +#define CMD_TEST_PREDEFINED 1 + +#define BUFFER_FILL_VALUE 0xFF + +/* + * Support for the special debugger read/write control methods. + * These methods are installed into the current namespace and are + * used to read and write the various namespace objects. The point + * is to force the AML interpreter do all of the work. + */ +#define ACPI_DB_READ_METHOD "\\_T98" +#define ACPI_DB_WRITE_METHOD "\\_T99" + +static acpi_handle read_handle = NULL; +static acpi_handle write_handle = NULL; + +/* ASL Definitions of the debugger read/write control methods */ + +#if 0 +definition_block("ssdt.aml", "SSDT", 2, "Intel", "DEBUG", 0x00000001) +{ + method(_T98, 1, not_serialized) { /* Read */ + return (de_ref_of(arg0)) + } +} + +definition_block("ssdt2.aml", "SSDT", 2, "Intel", "DEBUG", 0x00000001) +{ + method(_T99, 2, not_serialized) { /* Write */ + store(arg1, arg0) + } +} +#endif + +static unsigned char read_method_code[] = { + 0x53, 0x53, 0x44, 0x54, 0x2E, 0x00, 0x00, 0x00, /* 00000000 "SSDT...." */ + 0x02, 0xC9, 0x49, 0x6E, 0x74, 0x65, 0x6C, 0x00, /* 00000008 "..Intel." */ + 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x00, 0x00, /* 00000010 "DEBUG..." */ + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, /* 00000018 "....INTL" */ + 0x18, 0x12, 0x13, 0x20, 0x14, 0x09, 0x5F, 0x54, /* 00000020 "... .._T" */ + 0x39, 0x38, 0x01, 0xA4, 0x83, 0x68 /* 00000028 "98...h" */ +}; + +static unsigned char write_method_code[] = { + 0x53, 0x53, 0x44, 0x54, 0x2E, 0x00, 0x00, 0x00, /* 00000000 "SSDT...." */ + 0x02, 0x15, 0x49, 0x6E, 0x74, 0x65, 0x6C, 0x00, /* 00000008 "..Intel." */ + 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x00, 0x00, /* 00000010 "DEBUG..." */ + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, /* 00000018 "....INTL" */ + 0x18, 0x12, 0x13, 0x20, 0x14, 0x09, 0x5F, 0x54, /* 00000020 "... .._T" */ + 0x39, 0x39, 0x02, 0x70, 0x69, 0x68 /* 00000028 "99.pih" */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_test + * + * PARAMETERS: type_arg - Subcommand + * + * RETURN: None + * + * DESCRIPTION: Execute various debug tests. + * + * Note: Code is prepared for future expansion of the TEST command. + * + ******************************************************************************/ + +void acpi_db_execute_test(char *type_arg) +{ + u32 temp; + + acpi_ut_strupr(type_arg); + temp = acpi_db_match_argument(type_arg, acpi_db_test_types); + if (temp == ACPI_TYPE_NOT_FOUND) { + acpi_os_printf("Invalid or unsupported argument\n"); + return; + } + + switch (temp) { + case CMD_TEST_OBJECTS: + + acpi_db_test_all_objects(); + break; + + case CMD_TEST_PREDEFINED: + + acpi_db_evaluate_all_predefined_names(NULL); + break; + + default: + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_all_objects + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: This test implements the OBJECTS subcommand. It exercises the + * namespace by reading/writing/comparing all data objects such + * as integers, strings, buffers, fields, buffer fields, etc. + * + ******************************************************************************/ + +static void acpi_db_test_all_objects(void) +{ + acpi_status status; + + /* Install the debugger read-object control method if necessary */ + + if (!read_handle) { + status = acpi_install_method(read_method_code); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("%s, Could not install debugger read method\n", + acpi_format_exception(status)); + return; + } + + status = + acpi_get_handle(NULL, ACPI_DB_READ_METHOD, &read_handle); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not obtain handle for debug method %s\n", + ACPI_DB_READ_METHOD); + return; + } + } + + /* Install the debugger write-object control method if necessary */ + + if (!write_handle) { + status = acpi_install_method(write_method_code); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("%s, Could not install debugger write method\n", + acpi_format_exception(status)); + return; + } + + status = + acpi_get_handle(NULL, ACPI_DB_WRITE_METHOD, &write_handle); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not obtain handle for debug method %s\n", + ACPI_DB_WRITE_METHOD); + return; + } + } + + /* Walk the entire namespace, testing each supported named data object */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_test_one_object, + NULL, NULL, NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_one_object + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: Status + * + * DESCRIPTION: Test one namespace object. Supported types are Integer, + * String, Buffer, buffer_field, and field_unit. All other object + * types are simply ignored. + * + * Note: Support for Packages is not implemented. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_one_object(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_namespace_node *node; + union acpi_operand_object *obj_desc; + union acpi_operand_object *region_obj; + acpi_object_type local_type; + u32 bit_length = 0; + u32 byte_length = 0; + acpi_status status = AE_OK; + + node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + obj_desc = node->object; + + /* + * For the supported types, get the actual bit length or + * byte length. Map the type to one of Integer/String/Buffer. + */ + switch (node->type) { + case ACPI_TYPE_INTEGER: + + /* Integer width is either 32 or 64 */ + + local_type = ACPI_TYPE_INTEGER; + bit_length = acpi_gbl_integer_bit_width; + break; + + case ACPI_TYPE_STRING: + + local_type = ACPI_TYPE_STRING; + byte_length = obj_desc->string.length; + break; + + case ACPI_TYPE_BUFFER: + + local_type = ACPI_TYPE_BUFFER; + byte_length = obj_desc->buffer.length; + bit_length = byte_length * 8; + break; + + case ACPI_TYPE_FIELD_UNIT: + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + + local_type = ACPI_TYPE_INTEGER; + if (obj_desc) { + /* + * Returned object will be a Buffer if the field length + * is larger than the size of an Integer (32 or 64 bits + * depending on the DSDT version). + */ + bit_length = obj_desc->common_field.bit_length; + byte_length = ACPI_ROUND_BITS_UP_TO_BYTES(bit_length); + if (bit_length > acpi_gbl_integer_bit_width) { + local_type = ACPI_TYPE_BUFFER; + } + } + break; + + default: + + /* Ignore all other types */ + + return (AE_OK); + } + + /* Emit the common prefix: Type:Name */ + + acpi_os_printf("%14s: %4.4s", + acpi_ut_get_type_name(node->type), node->name.ascii); + if (!obj_desc) { + acpi_os_printf(" Ignoring, no attached object\n"); + return (AE_OK); + } + + /* + * Check for unsupported region types. Note: acpi_exec simulates + * access to system_memory, system_IO, PCI_Config, and EC. + */ + switch (node->type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + + region_obj = obj_desc->field.region_obj; + switch (region_obj->region.space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + case ACPI_ADR_SPACE_SYSTEM_IO: + case ACPI_ADR_SPACE_PCI_CONFIG: + case ACPI_ADR_SPACE_EC: + + break; + + default: + + acpi_os_printf + (" %s space is not supported [%4.4s]\n", + acpi_ut_get_region_name(region_obj->region. + space_id), + region_obj->region.node->name.ascii); + return (AE_OK); + } + break; + + default: + break; + } + + /* At this point, we have resolved the object to one of the major types */ + + switch (local_type) { + case ACPI_TYPE_INTEGER: + + status = acpi_db_test_integer_type(node, bit_length); + break; + + case ACPI_TYPE_STRING: + + status = acpi_db_test_string_type(node, byte_length); + break; + + case ACPI_TYPE_BUFFER: + + status = acpi_db_test_buffer_type(node, bit_length); + break; + + default: + + acpi_os_printf(" Ignoring, type not implemented (%2.2X)", + local_type); + break; + } + + switch (node->type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + + region_obj = obj_desc->field.region_obj; + acpi_os_printf(" (%s)", + acpi_ut_get_region_name(region_obj->region. + space_id)); + break; + + default: + break; + } + + acpi_os_printf("\n"); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_integer_type + * + * PARAMETERS: node - Parent NS node for the object + * bit_length - Actual length of the object. Used for + * support of arbitrary length field_unit + * and buffer_field objects. + * + * RETURN: Status + * + * DESCRIPTION: Test read/write for an Integer-valued object. Performs a + * write/read/compare of an arbitrary new value, then performs + * a write/read/compare of the original value. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length) +{ + union acpi_object *temp1 = NULL; + union acpi_object *temp2 = NULL; + union acpi_object *temp3 = NULL; + union acpi_object write_value; + u64 value_to_write; + acpi_status status; + + if (bit_length > 64) { + acpi_os_printf(" Invalid length for an Integer: %u", + bit_length); + return (AE_OK); + } + + /* Read the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp1); + if (ACPI_FAILURE(status)) { + return (status); + } + + acpi_os_printf(" (%4.4X/%3.3X) %8.8X%8.8X", + bit_length, ACPI_ROUND_BITS_UP_TO_BYTES(bit_length), + ACPI_FORMAT_UINT64(temp1->integer.value)); + + value_to_write = ACPI_UINT64_MAX >> (64 - bit_length); + if (temp1->integer.value == value_to_write) { + value_to_write = 0; + } + + /* Write a new value */ + + write_value.type = ACPI_TYPE_INTEGER; + write_value.integer.value = value_to_write; + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the new value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp2); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (temp2->integer.value != value_to_write) { + acpi_os_printf(" MISMATCH 2: %8.8X%8.8X, expecting %8.8X%8.8X", + ACPI_FORMAT_UINT64(temp2->integer.value), + ACPI_FORMAT_UINT64(value_to_write)); + } + + /* Write back the original value */ + + write_value.integer.value = temp1->integer.value; + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp3); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (temp3->integer.value != temp1->integer.value) { + acpi_os_printf(" MISMATCH 3: %8.8X%8.8X, expecting %8.8X%8.8X", + ACPI_FORMAT_UINT64(temp3->integer.value), + ACPI_FORMAT_UINT64(temp1->integer.value)); + } + +exit: + if (temp1) { + acpi_os_free(temp1); + } + if (temp2) { + acpi_os_free(temp2); + } + if (temp3) { + acpi_os_free(temp3); + } + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_buffer_type + * + * PARAMETERS: node - Parent NS node for the object + * bit_length - Actual length of the object. + * + * RETURN: Status + * + * DESCRIPTION: Test read/write for an Buffer-valued object. Performs a + * write/read/compare of an arbitrary new value, then performs + * a write/read/compare of the original value. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length) +{ + union acpi_object *temp1 = NULL; + union acpi_object *temp2 = NULL; + union acpi_object *temp3 = NULL; + u8 *buffer; + union acpi_object write_value; + acpi_status status; + u32 byte_length; + u32 i; + u8 extra_bits; + + byte_length = ACPI_ROUND_BITS_UP_TO_BYTES(bit_length); + if (byte_length == 0) { + acpi_os_printf(" Ignoring zero length buffer"); + return (AE_OK); + } + + /* Allocate a local buffer */ + + buffer = ACPI_ALLOCATE_ZEROED(byte_length); + if (!buffer) { + return (AE_NO_MEMORY); + } + + /* Read the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp1); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Emit a few bytes of the buffer */ + + acpi_os_printf(" (%4.4X/%3.3X)", bit_length, temp1->buffer.length); + for (i = 0; ((i < 4) && (i < byte_length)); i++) { + acpi_os_printf(" %2.2X", temp1->buffer.pointer[i]); + } + acpi_os_printf("... "); + + /* + * Write a new value. + * + * Handle possible extra bits at the end of the buffer. Can + * happen for field_units larger than an integer, but the bit + * count is not an integral number of bytes. Zero out the + * unused bits. + */ + memset(buffer, BUFFER_FILL_VALUE, byte_length); + extra_bits = bit_length % 8; + if (extra_bits) { + buffer[byte_length - 1] = ACPI_MASK_BITS_ABOVE(extra_bits); + } + + write_value.type = ACPI_TYPE_BUFFER; + write_value.buffer.length = byte_length; + write_value.buffer.pointer = buffer; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the new value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp2); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (memcmp(temp2->buffer.pointer, buffer, byte_length)) { + acpi_os_printf(" MISMATCH 2: New buffer value"); + } + + /* Write back the original value */ + + write_value.buffer.length = byte_length; + write_value.buffer.pointer = temp1->buffer.pointer; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp3); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (memcmp(temp1->buffer.pointer, temp3->buffer.pointer, byte_length)) { + acpi_os_printf(" MISMATCH 3: While restoring original buffer"); + } + +exit: + ACPI_FREE(buffer); + if (temp1) { + acpi_os_free(temp1); + } + if (temp2) { + acpi_os_free(temp2); + } + if (temp3) { + acpi_os_free(temp3); + } + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_string_type + * + * PARAMETERS: node - Parent NS node for the object + * byte_length - Actual length of the object. + * + * RETURN: Status + * + * DESCRIPTION: Test read/write for an String-valued object. Performs a + * write/read/compare of an arbitrary new value, then performs + * a write/read/compare of the original value. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length) +{ + union acpi_object *temp1 = NULL; + union acpi_object *temp2 = NULL; + union acpi_object *temp3 = NULL; + char *value_to_write = "Test String from AML Debugger"; + union acpi_object write_value; + acpi_status status; + + /* Read the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp1); + if (ACPI_FAILURE(status)) { + return (status); + } + + acpi_os_printf(" (%4.4X/%3.3X) \"%s\"", (temp1->string.length * 8), + temp1->string.length, temp1->string.pointer); + + /* Write a new value */ + + write_value.type = ACPI_TYPE_STRING; + write_value.string.length = strlen(value_to_write); + write_value.string.pointer = value_to_write; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the new value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp2); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (strcmp(temp2->string.pointer, value_to_write)) { + acpi_os_printf(" MISMATCH 2: %s, expecting %s", + temp2->string.pointer, value_to_write); + } + + /* Write back the original value */ + + write_value.string.length = strlen(temp1->string.pointer); + write_value.string.pointer = temp1->string.pointer; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp3); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (strcmp(temp1->string.pointer, temp3->string.pointer)) { + acpi_os_printf(" MISMATCH 3: %s, expecting %s", + temp3->string.pointer, temp1->string.pointer); + } + +exit: + if (temp1) { + acpi_os_free(temp1); + } + if (temp2) { + acpi_os_free(temp2); + } + if (temp3) { + acpi_os_free(temp3); + } + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_read_from_object + * + * PARAMETERS: node - Parent NS node for the object + * expected_type - Object type expected from the read + * value - Where the value read is returned + * + * RETURN: Status + * + * DESCRIPTION: Performs a read from the specified object by invoking the + * special debugger control method that reads the object. Thus, + * the AML interpreter is doing all of the work, increasing the + * validity of the test. + * + ******************************************************************************/ + +static acpi_status +acpi_db_read_from_object(struct acpi_namespace_node *node, + acpi_object_type expected_type, + union acpi_object **value) +{ + union acpi_object *ret_value; + struct acpi_object_list param_objects; + union acpi_object params[2]; + struct acpi_buffer return_obj; + acpi_status status; + + params[0].type = ACPI_TYPE_LOCAL_REFERENCE; + params[0].reference.actual_type = node->type; + params[0].reference.handle = ACPI_CAST_PTR(acpi_handle, node); + + param_objects.count = 1; + param_objects.pointer = params; + + return_obj.length = ACPI_ALLOCATE_BUFFER; + + acpi_gbl_method_executing = TRUE; + status = acpi_evaluate_object(read_handle, NULL, + ¶m_objects, &return_obj); + acpi_gbl_method_executing = FALSE; + + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not read from object, %s", + acpi_format_exception(status)); + return (status); + } + + ret_value = (union acpi_object *)return_obj.pointer; + + switch (ret_value->type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + /* + * Did we receive the type we wanted? Most important for the + * Integer/Buffer case (when a field is larger than an Integer, + * it should return a Buffer). + */ + if (ret_value->type != expected_type) { + acpi_os_printf + (" Type mismatch: Expected %s, Received %s", + acpi_ut_get_type_name(expected_type), + acpi_ut_get_type_name(ret_value->type)); + + return (AE_TYPE); + } + + *value = ret_value; + break; + + default: + + acpi_os_printf(" Unsupported return object type, %s", + acpi_ut_get_type_name(ret_value->type)); + + acpi_os_free(return_obj.pointer); + return (AE_TYPE); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_write_to_object + * + * PARAMETERS: node - Parent NS node for the object + * value - Value to be written + * + * RETURN: Status + * + * DESCRIPTION: Performs a write to the specified object by invoking the + * special debugger control method that writes the object. Thus, + * the AML interpreter is doing all of the work, increasing the + * validity of the test. + * + ******************************************************************************/ + +static acpi_status +acpi_db_write_to_object(struct acpi_namespace_node *node, + union acpi_object *value) +{ + struct acpi_object_list param_objects; + union acpi_object params[2]; + acpi_status status; + + params[0].type = ACPI_TYPE_LOCAL_REFERENCE; + params[0].reference.actual_type = node->type; + params[0].reference.handle = ACPI_CAST_PTR(acpi_handle, node); + + /* Copy the incoming user parameter */ + + memcpy(¶ms[1], value, sizeof(union acpi_object)); + + param_objects.count = 2; + param_objects.pointer = params; + + acpi_gbl_method_executing = TRUE; + status = acpi_evaluate_object(write_handle, NULL, ¶m_objects, NULL); + acpi_gbl_method_executing = FALSE; + + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not write to object, %s", + acpi_format_exception(status)); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_evaluate_all_predefined_names + * + * PARAMETERS: count_arg - Max number of methods to execute + * + * RETURN: None + * + * DESCRIPTION: Namespace batch execution. Execute predefined names in the + * namespace, up to the max count, if specified. + * + ******************************************************************************/ + +static void acpi_db_evaluate_all_predefined_names(char *count_arg) +{ + struct acpi_db_execute_walk info; + + info.count = 0; + info.max_count = ACPI_UINT32_MAX; + + if (count_arg) { + info.max_count = strtoul(count_arg, NULL, 0); + } + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_evaluate_one_predefined_name, NULL, + (void *)&info, NULL); + + acpi_os_printf("Evaluated %u predefined names in the namespace\n", + info.count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_evaluate_one_predefined_name + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Batch execution module. Currently only executes predefined + * ACPI names. + * + ******************************************************************************/ + +static acpi_status +acpi_db_evaluate_one_predefined_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + struct acpi_db_execute_walk *info = + (struct acpi_db_execute_walk *)context; + char *pathname; + const union acpi_predefined_info *predefined; + struct acpi_device_info *obj_info; + struct acpi_object_list param_objects; + union acpi_object params[ACPI_METHOD_NUM_ARGS]; + union acpi_object *this_param; + struct acpi_buffer return_obj; + acpi_status status; + u16 arg_type_list; + u8 arg_count; + u8 arg_type; + u32 i; + + /* The name must be a predefined ACPI name */ + + predefined = acpi_ut_match_predefined_method(node->name.ascii); + if (!predefined) { + return (AE_OK); + } + + if (node->type == ACPI_TYPE_LOCAL_SCOPE) { + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + if (!pathname) { + return (AE_OK); + } + + /* Get the object info for number of method parameters */ + + status = acpi_get_object_info(obj_handle, &obj_info); + if (ACPI_FAILURE(status)) { + ACPI_FREE(pathname); + return (status); + } + + param_objects.count = 0; + param_objects.pointer = NULL; + + if (obj_info->type == ACPI_TYPE_METHOD) { + + /* Setup default parameters (with proper types) */ + + arg_type_list = predefined->info.argument_list; + arg_count = METHOD_GET_ARG_COUNT(arg_type_list); + + /* + * Setup the ACPI-required number of arguments, regardless of what + * the actual method defines. If there is a difference, then the + * method is wrong and a warning will be issued during execution. + */ + this_param = params; + for (i = 0; i < arg_count; i++) { + arg_type = METHOD_GET_NEXT_TYPE(arg_type_list); + this_param->type = arg_type; + + switch (arg_type) { + case ACPI_TYPE_INTEGER: + + this_param->integer.value = 1; + break; + + case ACPI_TYPE_STRING: + + this_param->string.pointer = + "This is the default argument string"; + this_param->string.length = + strlen(this_param->string.pointer); + break; + + case ACPI_TYPE_BUFFER: + + this_param->buffer.pointer = (u8 *)params; /* just a garbage buffer */ + this_param->buffer.length = 48; + break; + + case ACPI_TYPE_PACKAGE: + + this_param->package.elements = NULL; + this_param->package.count = 0; + break; + + default: + + acpi_os_printf + ("%s: Unsupported argument type: %u\n", + pathname, arg_type); + break; + } + + this_param++; + } + + param_objects.count = arg_count; + param_objects.pointer = params; + } + + ACPI_FREE(obj_info); + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + /* Do the actual method execution */ + + acpi_gbl_method_executing = TRUE; + + status = acpi_evaluate_object(node, NULL, ¶m_objects, &return_obj); + + acpi_os_printf("%-32s returned %s\n", + pathname, acpi_format_exception(status)); + acpi_gbl_method_executing = FALSE; + ACPI_FREE(pathname); + + /* Ignore status from method execution */ + + status = AE_OK; + + /* Update count, check if we have executed enough methods */ + + info->count++; + if (info->count >= info->max_count) { + status = AE_CTRL_TERMINATE; + } + + return (status); +} diff --git a/drivers/acpi/acpica/dbutils.c b/drivers/acpi/acpica/dbutils.c new file mode 100644 index 000000000000..86790e080139 --- /dev/null +++ b/drivers/acpi/acpica/dbutils.c @@ -0,0 +1,457 @@ +/******************************************************************************* + * + * Module Name: dbutils - AML debugger utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbutils") + +/* Local prototypes */ +#ifdef ACPI_OBSOLETE_FUNCTIONS +acpi_status acpi_db_second_pass_parse(union acpi_parse_object *root); + +void acpi_db_dump_buffer(u32 address); +#endif + +static char *gbl_hex_to_ascii = "0123456789ABCDEF"; + +/******************************************************************************* + * + * FUNCTION: acpi_db_match_argument + * + * PARAMETERS: user_argument - User command line + * arguments - Array of commands to match against + * + * RETURN: Index into command array or ACPI_TYPE_NOT_FOUND if not found + * + * DESCRIPTION: Search command array for a command match + * + ******************************************************************************/ + +acpi_object_type +acpi_db_match_argument(char *user_argument, + struct acpi_db_argument_info *arguments) +{ + u32 i; + + if (!user_argument || user_argument[0] == 0) { + return (ACPI_TYPE_NOT_FOUND); + } + + for (i = 0; arguments[i].name; i++) { + if (strstr(arguments[i].name, user_argument) == + arguments[i].name) { + return (i); + } + } + + /* Argument not recognized */ + + return (ACPI_TYPE_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_output_destination + * + * PARAMETERS: output_flags - Current flags word + * + * RETURN: None + * + * DESCRIPTION: Set the current destination for debugger output. Also sets + * the debug output level accordingly. + * + ******************************************************************************/ + +void acpi_db_set_output_destination(u32 output_flags) +{ + + acpi_gbl_db_output_flags = (u8)output_flags; + + if ((output_flags & ACPI_DB_REDIRECTABLE_OUTPUT) && + acpi_gbl_db_output_to_file) { + acpi_dbg_level = acpi_gbl_db_debug_level; + } else { + acpi_dbg_level = acpi_gbl_db_console_debug_level; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_external_object + * + * PARAMETERS: obj_desc - External ACPI object to dump + * level - Nesting level. + * + * RETURN: None + * + * DESCRIPTION: Dump the contents of an ACPI external object + * + ******************************************************************************/ + +void acpi_db_dump_external_object(union acpi_object *obj_desc, u32 level) +{ + u32 i; + + if (!obj_desc) { + acpi_os_printf("[Null Object]\n"); + return; + } + + for (i = 0; i < level; i++) { + acpi_os_printf(" "); + } + + switch (obj_desc->type) { + case ACPI_TYPE_ANY: + + acpi_os_printf("[Null Object] (Type=0)\n"); + break; + + case ACPI_TYPE_INTEGER: + + acpi_os_printf("[Integer] = %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(obj_desc->integer.value)); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("[String] Length %.2X = ", + obj_desc->string.length); + acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX); + acpi_os_printf("\n"); + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("[Buffer] Length %.2X = ", + obj_desc->buffer.length); + if (obj_desc->buffer.length) { + if (obj_desc->buffer.length > 16) { + acpi_os_printf("\n"); + } + acpi_ut_debug_dump_buffer(ACPI_CAST_PTR + (u8, + obj_desc->buffer.pointer), + obj_desc->buffer.length, + DB_BYTE_DISPLAY, _COMPONENT); + } else { + acpi_os_printf("\n"); + } + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("[Package] Contains %u Elements:\n", + obj_desc->package.count); + + for (i = 0; i < obj_desc->package.count; i++) { + acpi_db_dump_external_object(&obj_desc->package. + elements[i], level + 1); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[Object Reference] = "); + acpi_db_display_internal_object(obj_desc->reference.handle, + NULL); + break; + + case ACPI_TYPE_PROCESSOR: + + acpi_os_printf("[Processor]\n"); + break; + + case ACPI_TYPE_POWER: + + acpi_os_printf("[Power Resource]\n"); + break; + + default: + + acpi_os_printf("[Unknown Type] %X\n", obj_desc->type); + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_prep_namestring + * + * PARAMETERS: name - String to prepare + * + * RETURN: None + * + * DESCRIPTION: Translate all forward slashes and dots to backslashes. + * + ******************************************************************************/ + +void acpi_db_prep_namestring(char *name) +{ + + if (!name) { + return; + } + + acpi_ut_strupr(name); + + /* Convert a leading forward slash to a backslash */ + + if (*name == '/') { + *name = '\\'; + } + + /* Ignore a leading backslash, this is the root prefix */ + + if (ACPI_IS_ROOT_PREFIX(*name)) { + name++; + } + + /* Convert all slash path separators to dots */ + + while (*name) { + if ((*name == '/') || (*name == '\\')) { + *name = '.'; + } + + name++; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_local_ns_lookup + * + * PARAMETERS: name - Name to lookup + * + * RETURN: Pointer to a namespace node, null on failure + * + * DESCRIPTION: Lookup a name in the ACPI namespace + * + * Note: Currently begins search from the root. Could be enhanced to use + * the current prefix (scope) node as the search beginning point. + * + ******************************************************************************/ + +struct acpi_namespace_node *acpi_db_local_ns_lookup(char *name) +{ + char *internal_path; + acpi_status status; + struct acpi_namespace_node *node = NULL; + + acpi_db_prep_namestring(name); + + /* Build an internal namestring */ + + status = acpi_ns_internalize_name(name, &internal_path); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Invalid namestring: %s\n", name); + return (NULL); + } + + /* + * Lookup the name. + * (Uses root node as the search starting point) + */ + status = acpi_ns_lookup(NULL, internal_path, ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, + ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE, + NULL, &node); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not locate name: %s, %s\n", + name, acpi_format_exception(status)); + } + + ACPI_FREE(internal_path); + return (node); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_uint32_to_hex_string + * + * PARAMETERS: value - The value to be converted to string + * buffer - Buffer for result (not less than 11 bytes) + * + * RETURN: None + * + * DESCRIPTION: Convert the unsigned 32-bit value to the hexadecimal image + * + * NOTE: It is the caller's responsibility to ensure that the length of buffer + * is sufficient. + * + ******************************************************************************/ + +void acpi_db_uint32_to_hex_string(u32 value, char *buffer) +{ + int i; + + if (value == 0) { + strcpy(buffer, "0"); + return; + } + + buffer[8] = '\0'; + + for (i = 7; i >= 0; i--) { + buffer[i] = gbl_hex_to_ascii[value & 0x0F]; + value = value >> 4; + } +} + +#ifdef ACPI_OBSOLETE_FUNCTIONS +/******************************************************************************* + * + * FUNCTION: acpi_db_second_pass_parse + * + * PARAMETERS: root - Root of the parse tree + * + * RETURN: Status + * + * DESCRIPTION: Second pass parse of the ACPI tables. We need to wait until + * second pass to parse the control methods + * + ******************************************************************************/ + +acpi_status acpi_db_second_pass_parse(union acpi_parse_object *root) +{ + union acpi_parse_object *op = root; + union acpi_parse_object *method; + union acpi_parse_object *search_op; + union acpi_parse_object *start_op; + acpi_status status = AE_OK; + u32 base_aml_offset; + struct acpi_walk_state *walk_state; + + ACPI_FUNCTION_ENTRY(); + + acpi_os_printf("Pass two parse ....\n"); + + while (op) { + if (op->common.aml_opcode == AML_METHOD_OP) { + method = op; + + /* Create a new walk state for the parse */ + + walk_state = + acpi_ds_create_walk_state(0, NULL, NULL, NULL); + if (!walk_state) { + return (AE_NO_MEMORY); + } + + /* Init the Walk State */ + + walk_state->parser_state.aml = + walk_state->parser_state.aml_start = + method->named.data; + walk_state->parser_state.aml_end = + walk_state->parser_state.pkg_end = + method->named.data + method->named.length; + walk_state->parser_state.start_scope = op; + + walk_state->descending_callback = + acpi_ds_load1_begin_op; + walk_state->ascending_callback = acpi_ds_load1_end_op; + + /* Perform the AML parse */ + + status = acpi_ps_parse_aml(walk_state); + + base_aml_offset = + (method->common.value.arg)->common.aml_offset + 1; + start_op = (method->common.value.arg)->common.next; + search_op = start_op; + + while (search_op) { + search_op->common.aml_offset += base_aml_offset; + search_op = + acpi_ps_get_depth_next(start_op, search_op); + } + } + + if (op->common.aml_opcode == AML_REGION_OP) { + + /* TBD: [Investigate] this isn't quite the right thing to do! */ + /* + * + * Method = (ACPI_DEFERRED_OP *) Op; + * Status = acpi_ps_parse_aml (Op, Method->Body, Method->body_length); + */ + } + + if (ACPI_FAILURE(status)) { + break; + } + + op = acpi_ps_get_depth_next(root, op); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_buffer + * + * PARAMETERS: address - Pointer to the buffer + * + * RETURN: None + * + * DESCRIPTION: Print a portion of a buffer + * + ******************************************************************************/ + +void acpi_db_dump_buffer(u32 address) +{ + + acpi_os_printf("\nLocation %X:\n", address); + + acpi_dbg_level |= ACPI_LV_TABLES; + acpi_ut_debug_dump_buffer(ACPI_TO_POINTER(address), 64, DB_BYTE_DISPLAY, + ACPI_UINT32_MAX); +} +#endif diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c new file mode 100644 index 000000000000..26023bd95f56 --- /dev/null +++ b/drivers/acpi/acpica/dbxface.c @@ -0,0 +1,467 @@ +/******************************************************************************* + * + * Module Name: dbxface - AML Debugger external interfaces + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbxface") + +/* Local prototypes */ +static acpi_status +acpi_db_start_command(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +#ifdef ACPI_OBSOLETE_FUNCTIONS +void acpi_db_method_end(struct acpi_walk_state *walk_state); +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_start_command + * + * PARAMETERS: walk_state - Current walk + * op - Current executing Op, from AML interpreter + * + * RETURN: Status + * + * DESCRIPTION: Enter debugger command loop + * + ******************************************************************************/ + +static acpi_status +acpi_db_start_command(struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + + /* TBD: [Investigate] are there namespace locking issues here? */ + + /* acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); */ + + /* Go into the command loop and await next user command */ + + acpi_gbl_method_executing = TRUE; + status = AE_CTRL_TRUE; + while (status == AE_CTRL_TRUE) { + if (acpi_gbl_debugger_configuration == DEBUGGER_MULTI_THREADED) { + + /* Handshake with the front-end that gets user command lines */ + + acpi_os_release_mutex(acpi_gbl_db_command_complete); + + status = + acpi_os_acquire_mutex(acpi_gbl_db_command_ready, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + } else { + /* Single threaded, we must get a command line ourselves */ + + /* Force output to console until a command is entered */ + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + + /* Different prompt if method is executing */ + + if (!acpi_gbl_method_executing) { + acpi_os_printf("%1c ", + ACPI_DEBUGGER_COMMAND_PROMPT); + } else { + acpi_os_printf("%1c ", + ACPI_DEBUGGER_EXECUTE_PROMPT); + } + + /* Get the user input line */ + + status = acpi_os_get_line(acpi_gbl_db_line_buf, + ACPI_DB_LINE_BUFFER_SIZE, + NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While parsing command line")); + return (status); + } + } + + status = + acpi_db_command_dispatch(acpi_gbl_db_line_buf, walk_state, + op); + } + + /* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */ + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_single_step + * + * PARAMETERS: walk_state - Current walk + * op - Current executing op (from aml interpreter) + * opcode_class - Class of the current AML Opcode + * + * RETURN: Status + * + * DESCRIPTION: Called just before execution of an AML opcode. + * + ******************************************************************************/ + +acpi_status +acpi_db_single_step(struct acpi_walk_state * walk_state, + union acpi_parse_object * op, u32 opcode_class) +{ + union acpi_parse_object *next; + acpi_status status = AE_OK; + u32 original_debug_level; + union acpi_parse_object *display_op; + union acpi_parse_object *parent_op; + u32 aml_offset; + + ACPI_FUNCTION_ENTRY(); + + /* Check the abort flag */ + + if (acpi_gbl_abort_method) { + acpi_gbl_abort_method = FALSE; + return (AE_ABORT_METHOD); + } + + aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml, + walk_state->parser_state.aml_start); + + /* Check for single-step breakpoint */ + + if (walk_state->method_breakpoint && + (walk_state->method_breakpoint <= aml_offset)) { + + /* Check if the breakpoint has been reached or passed */ + /* Hit the breakpoint, resume single step, reset breakpoint */ + + acpi_os_printf("***Break*** at AML offset %X\n", aml_offset); + acpi_gbl_cm_single_step = TRUE; + acpi_gbl_step_to_next_call = FALSE; + walk_state->method_breakpoint = 0; + } + + /* Check for user breakpoint (Must be on exact Aml offset) */ + + else if (walk_state->user_breakpoint && + (walk_state->user_breakpoint == aml_offset)) { + acpi_os_printf("***UserBreakpoint*** at AML offset %X\n", + aml_offset); + acpi_gbl_cm_single_step = TRUE; + acpi_gbl_step_to_next_call = FALSE; + walk_state->method_breakpoint = 0; + } + + /* + * Check if this is an opcode that we are interested in -- + * namely, opcodes that have arguments + */ + if (op->common.aml_opcode == AML_INT_NAMEDFIELD_OP) { + return (AE_OK); + } + + switch (opcode_class) { + case AML_CLASS_UNKNOWN: + case AML_CLASS_ARGUMENT: /* constants, literals, etc. do nothing */ + + return (AE_OK); + + default: + + /* All other opcodes -- continue */ + break; + } + + /* + * Under certain debug conditions, display this opcode and its operands + */ + if ((acpi_gbl_db_output_to_file) || + (acpi_gbl_cm_single_step) || (acpi_dbg_level & ACPI_LV_PARSE)) { + if ((acpi_gbl_db_output_to_file) || + (acpi_dbg_level & ACPI_LV_PARSE)) { + acpi_os_printf + ("\n[AmlDebug] Next AML Opcode to execute:\n"); + } + + /* + * Display this op (and only this op - zero out the NEXT field + * temporarily, and disable parser trace output for the duration of + * the display because we don't want the extraneous debug output) + */ + original_debug_level = acpi_dbg_level; + acpi_dbg_level &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS); + next = op->common.next; + op->common.next = NULL; + + display_op = op; + parent_op = op->common.parent; + if (parent_op) { + if ((walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_PREDICATE_EXECUTING)) { + /* + * We are executing the predicate of an IF or WHILE statement + * Search upwards for the containing IF or WHILE so that the + * entire predicate can be displayed. + */ + while (parent_op) { + if ((parent_op->common.aml_opcode == + AML_IF_OP) + || (parent_op->common.aml_opcode == + AML_WHILE_OP)) { + display_op = parent_op; + break; + } + parent_op = parent_op->common.parent; + } + } else { + while (parent_op) { + if ((parent_op->common.aml_opcode == + AML_IF_OP) + || (parent_op->common.aml_opcode == + AML_ELSE_OP) + || (parent_op->common.aml_opcode == + AML_SCOPE_OP) + || (parent_op->common.aml_opcode == + AML_METHOD_OP) + || (parent_op->common.aml_opcode == + AML_WHILE_OP)) { + break; + } + display_op = parent_op; + parent_op = parent_op->common.parent; + } + } + } + + /* Now we can display it */ + +#ifdef ACPI_DISASSEMBLER + acpi_dm_disassemble(walk_state, display_op, ACPI_UINT32_MAX); +#endif + + if ((op->common.aml_opcode == AML_IF_OP) || + (op->common.aml_opcode == AML_WHILE_OP)) { + if (walk_state->control_state->common.value) { + acpi_os_printf + ("Predicate = [True], IF block was executed\n"); + } else { + acpi_os_printf + ("Predicate = [False], Skipping IF block\n"); + } + } else if (op->common.aml_opcode == AML_ELSE_OP) { + acpi_os_printf + ("Predicate = [False], ELSE block was executed\n"); + } + + /* Restore everything */ + + op->common.next = next; + acpi_os_printf("\n"); + if ((acpi_gbl_db_output_to_file) || + (acpi_dbg_level & ACPI_LV_PARSE)) { + acpi_os_printf("\n"); + } + acpi_dbg_level = original_debug_level; + } + + /* If we are not single stepping, just continue executing the method */ + + if (!acpi_gbl_cm_single_step) { + return (AE_OK); + } + + /* + * If we are executing a step-to-call command, + * Check if this is a method call. + */ + if (acpi_gbl_step_to_next_call) { + if (op->common.aml_opcode != AML_INT_METHODCALL_OP) { + + /* Not a method call, just keep executing */ + + return (AE_OK); + } + + /* Found a method call, stop executing */ + + acpi_gbl_step_to_next_call = FALSE; + } + + /* + * If the next opcode is a method call, we will "step over" it + * by default. + */ + if (op->common.aml_opcode == AML_INT_METHODCALL_OP) { + + /* Force no more single stepping while executing called method */ + + acpi_gbl_cm_single_step = FALSE; + + /* + * Set the breakpoint on/before the call, it will stop execution + * as soon as we return + */ + walk_state->method_breakpoint = 1; /* Must be non-zero! */ + } + + status = acpi_db_start_command(walk_state, op); + + /* User commands complete, continue execution of the interrupted method */ + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_initialize_debugger + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Init and start debugger + * + ******************************************************************************/ + +acpi_status acpi_initialize_debugger(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_initialize_debugger); + + /* Init globals */ + + acpi_gbl_db_buffer = NULL; + acpi_gbl_db_filename = NULL; + acpi_gbl_db_output_to_file = FALSE; + + acpi_gbl_db_debug_level = ACPI_LV_VERBOSITY2; + acpi_gbl_db_console_debug_level = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES; + acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT; + + acpi_gbl_db_opt_no_ini_methods = FALSE; + + acpi_gbl_db_buffer = acpi_os_allocate(ACPI_DEBUG_BUFFER_SIZE); + if (!acpi_gbl_db_buffer) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + memset(acpi_gbl_db_buffer, 0, ACPI_DEBUG_BUFFER_SIZE); + + /* Initial scope is the root */ + + acpi_gbl_db_scope_buf[0] = AML_ROOT_PREFIX; + acpi_gbl_db_scope_buf[1] = 0; + acpi_gbl_db_scope_node = acpi_gbl_root_node; + + /* + * If configured for multi-thread support, the debug executor runs in + * a separate thread so that the front end can be in another address + * space, environment, or even another machine. + */ + if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { + + /* These were created with one unit, grab it */ + + status = acpi_os_acquire_mutex(acpi_gbl_db_command_complete, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not get debugger mutex\n"); + return_ACPI_STATUS(status); + } + + status = acpi_os_acquire_mutex(acpi_gbl_db_command_ready, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not get debugger mutex\n"); + return_ACPI_STATUS(status); + } + + /* Create the debug execution thread to execute commands */ + + status = acpi_os_execute(OSL_DEBUGGER_THREAD, + acpi_db_execute_thread, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not start debugger thread")); + return_ACPI_STATUS(status); + } + } + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_initialize_debugger) + +/******************************************************************************* + * + * FUNCTION: acpi_terminate_debugger + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Stop debugger + * + ******************************************************************************/ +void acpi_terminate_debugger(void) +{ + + if (acpi_gbl_db_buffer) { + acpi_os_free(acpi_gbl_db_buffer); + acpi_gbl_db_buffer = NULL; + } + + /* Ensure that debug output is now disabled */ + + acpi_gbl_db_output_flags = ACPI_DB_DISABLE_OUTPUT; +} + +ACPI_EXPORT_SYMBOL(acpi_terminate_debugger) -- cgit v1.2.3 From af08f9cc5073eee875016d28730c99ec86da4198 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:32 +0800 Subject: ACPICA: Debugger: Fix "quit/exit" command by cleaning up user commands termination logic ACPICA commit 0dd68e16274cd38224aa4781eddc57dc2cbaa108 The quit/exit commands shouldn't invoke acpi_terminate_debugger() and acpi_terminate() right in the user command loop, because when the debugger exits, the kernel ACPI subsystem shouldn't be terminated (acpi_terminate()) and the debugger should only be terminated by its users (acpi_terminate_debugger()) rather than being terminated itself. Leaving such invocations causes kernel panic when the debugger is shipped in the Linux kernel. This patch fixes this issue. Lv Zheng. Link: https://github.com/acpica/acpica/commit/0dd68e16 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acglobal.h | 3 ++- drivers/acpi/acpica/dbinput.c | 16 ++++------------ drivers/acpi/acpica/dbxface.c | 20 ++++++++++++++++++++ drivers/acpi/acpica/utinit.c | 2 -- drivers/acpi/acpica/utxface.c | 4 ---- 5 files changed, 26 insertions(+), 19 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 71a3c40dbf20..e57686533f6d 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -325,7 +325,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list); #ifdef ACPI_DEBUGGER -ACPI_INIT_GLOBAL(u8, acpi_gbl_db_terminate_threads, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE); @@ -337,6 +336,8 @@ ACPI_GLOBAL(char *, acpi_gbl_db_filename); ACPI_GLOBAL(u32, acpi_gbl_db_debug_level); ACPI_GLOBAL(u32, acpi_gbl_db_console_debug_level); ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_db_scope_node); +ACPI_GLOBAL(u8, acpi_gbl_db_terminate_loop); +ACPI_GLOBAL(u8, acpi_gbl_db_threads_terminated); ACPI_GLOBAL(char *, acpi_gbl_db_args[ACPI_DEBUGGER_MAX_ARGS]); ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]); diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c index 7f1b6ec62668..f8cddd682af2 100644 --- a/drivers/acpi/acpica/dbinput.c +++ b/drivers/acpi/acpica/dbinput.c @@ -694,7 +694,7 @@ acpi_db_command_dispatch(char *input_buffer, /* If acpi_terminate has been called, terminate this thread */ - if (acpi_gbl_db_terminate_threads) { + if (acpi_gbl_db_terminate_loop) { return (AE_CTRL_TERMINATE); } @@ -1116,7 +1116,7 @@ acpi_db_command_dispatch(char *input_buffer, #ifdef ACPI_APPLICATION acpi_db_close_debug_file(); #endif - acpi_gbl_db_terminate_threads = TRUE; + acpi_gbl_db_terminate_loop = TRUE; return (AE_CTRL_TERMINATE); case CMD_NOT_FOUND: @@ -1166,6 +1166,7 @@ void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context) acpi_os_release_mutex(acpi_gbl_db_command_complete); } + acpi_gbl_db_threads_terminated = TRUE; } /******************************************************************************* @@ -1212,7 +1213,7 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op) /* TBD: [Restructure] Need a separate command line buffer for step mode */ - while (!acpi_gbl_db_terminate_threads) { + while (!acpi_gbl_db_terminate_loop) { /* Force output to console until a command is entered */ @@ -1261,14 +1262,5 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op) } } - /* Shut down the debugger */ - - acpi_terminate_debugger(); - - /* - * Only this thread (the original thread) should actually terminate the - * subsystem, because all the semaphores are deleted during termination - */ - status = acpi_terminate(); return (status); } diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c index 26023bd95f56..bef5f4ed22a0 100644 --- a/drivers/acpi/acpica/dbxface.c +++ b/drivers/acpi/acpica/dbxface.c @@ -401,6 +401,10 @@ acpi_status acpi_initialize_debugger(void) acpi_gbl_db_scope_buf[1] = 0; acpi_gbl_db_scope_node = acpi_gbl_root_node; + /* Initialize user commands loop */ + + acpi_gbl_db_terminate_loop = FALSE; + /* * If configured for multi-thread support, the debug executor runs in * a separate thread so that the front end can be in another address @@ -426,11 +430,13 @@ acpi_status acpi_initialize_debugger(void) /* Create the debug execution thread to execute commands */ + acpi_gbl_db_threads_terminated = FALSE; status = acpi_os_execute(OSL_DEBUGGER_THREAD, acpi_db_execute_thread, NULL); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Could not start debugger thread")); + acpi_gbl_db_threads_terminated = TRUE; return_ACPI_STATUS(status); } } @@ -454,6 +460,20 @@ ACPI_EXPORT_SYMBOL(acpi_initialize_debugger) void acpi_terminate_debugger(void) { + /* Terminate the AML Debugger */ + + acpi_gbl_db_terminate_loop = TRUE; + + if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { + acpi_os_release_mutex(acpi_gbl_db_command_ready); + + /* Wait the AML Debugger threads */ + + while (!acpi_gbl_db_threads_terminated) { + acpi_os_sleep(100); + } + } + if (acpi_gbl_db_buffer) { acpi_os_free(acpi_gbl_db_buffer); acpi_gbl_db_buffer = NULL; diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index 28ab3a1d5ec1..d8699df8914c 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -241,8 +241,6 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_disable_mem_tracking = FALSE; #endif - ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = FALSE); - return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 4f332815db00..f183daf938b7 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -80,10 +80,6 @@ acpi_status __init acpi_terminate(void) acpi_gbl_startup_flags = 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); - /* Terminate the AML Debugger if present */ - - ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = TRUE); - /* Shutdown and free all resources */ acpi_ut_subsystem_shutdown(); -- cgit v1.2.3 From 086ab742ac97b45ee64507b32b3d1ecbdbc56b39 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:42 +0800 Subject: ACPICA: Debugger: Fix "terminate" command by cleaning up subsystem shutdown logic ACPICA commit 7e823714911480be47e310fb1b3590d289b9fd99 Segmentation fault can be seen for executing the "terminate" command. This is because acpi_ut_subsystem_shutdown() is errnously called multiple times. This patch cleans up acpi_ut_subsystem_shutdown() logics to fix this issue. Lv Zheng. Link: https://github.com/acpica/acpica/commit/7e823714 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/dbinput.c | 3 ++- drivers/acpi/acpica/utinit.c | 13 +++++++++++++ drivers/acpi/acpica/utxface.c | 13 ------------- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c index f8cddd682af2..0480254437f1 100644 --- a/drivers/acpi/acpica/dbinput.c +++ b/drivers/acpi/acpica/dbinput.c @@ -1074,6 +1074,7 @@ acpi_db_command_dispatch(char *input_buffer, * re-creating the semaphores! */ + acpi_gbl_db_terminate_loop = TRUE; /* acpi_initialize (NULL); */ break; @@ -1151,7 +1152,7 @@ void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context) acpi_status status = AE_OK; acpi_status Mstatus; - while (status != AE_CTRL_TERMINATE) { + while (status != AE_CTRL_TERMINATE && !acpi_gbl_db_terminate_loop) { acpi_gbl_method_executing = FALSE; acpi_gbl_step_to_next_call = FALSE; diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index d8699df8914c..ccd0745f011e 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -282,6 +282,19 @@ void acpi_ut_subsystem_shutdown(void) { ACPI_FUNCTION_TRACE(ut_subsystem_shutdown); + /* Just exit if subsystem is already shutdown */ + + if (acpi_gbl_shutdown) { + ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated")); + return_VOID; + } + + /* Subsystem appears active, go ahead and shut it down */ + + acpi_gbl_shutdown = TRUE; + acpi_gbl_startup_flags = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); + #ifndef ACPI_ASL_COMPILER /* Close the acpi_event Handling */ diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index f183daf938b7..a6b0eb034fc0 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -67,19 +67,6 @@ acpi_status __init acpi_terminate(void) ACPI_FUNCTION_TRACE(acpi_terminate); - /* Just exit if subsystem is already shutdown */ - - if (acpi_gbl_shutdown) { - ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated")); - return_ACPI_STATUS(AE_OK); - } - - /* Subsystem appears active, go ahead and shut it down */ - - acpi_gbl_shutdown = TRUE; - acpi_gbl_startup_flags = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); - /* Shutdown and free all resources */ acpi_ut_subsystem_shutdown(); -- cgit v1.2.3 From f988f24ee7931d3487b36cc0c29164296bf2191e Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:50 +0800 Subject: ACPICA: Debugger: Add thread ID support so that single step mode can only apply to the debugger thread When the debugger is running in the kernel mode, acpi_db_single_step() may also be invoked by the kernel runtime code path but the single stepping command prompt may be erronously logged as the kernel logs and runtime code path cannot proceed. This patch fixes this issue by adding acpi_gbl_db_thread_id for the debugger thread and preventing acpi_db_single_step() to be invoked from other threads. It is not suitable to add acpi_thread_id parameter for acpi_os_execute() as the function may be implemented as work queue on some hosts. So it is better to let the hosts invoke acpi_set_debugger_thread_id(). Currently acpiexec is not configured as DEBUGGER_MULTI_THREADED, but we can do this. When we do this, it is better to invoke acpi_set_debugger_thread_id() in acpi_os_execute() when the execution type is OSL_DEBUGGER_MAIN_THREAD. The support should look like: create_thread(&tid); if (type == OSL_DEBUGGER_MAIN_THREAD) acpi_set_debugger_thread_id(tid); resume_thread(tid); Similarly, semop() may be used for pthread implementation. But this patch simply skips debugger thread ID check for application instead of introducing such complications as there is no need to skip acpi_db_single_step() for an application debugger - acpiexec. Note that the debugger thread ID can also be used by acpi_os_printf() to filter out debugger output. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acglobal.h | 1 + drivers/acpi/acpica/aclocal.h | 8 ++++++++ drivers/acpi/acpica/dbexec.c | 3 ++- drivers/acpi/acpica/dbxface.c | 28 +++++++++++++++++++++++++++- include/acpi/acpiosxf.h | 3 ++- include/acpi/acpixf.h | 2 ++ 6 files changed, 42 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index e57686533f6d..faa97604d878 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -327,6 +327,7 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list); ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE); +ACPI_INIT_GLOBAL(acpi_thread_id, acpi_gbl_db_thread_id, ACPI_INVALID_THREAD_ID); ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods); ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_region_support); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 4e41b43ebad3..e1dd784d8515 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -109,6 +109,14 @@ struct acpi_rw_lock { #define ACPI_MUTEX_NOT_ACQUIRED (acpi_thread_id) 0 +/* This Thread ID means an invalid thread ID */ + +#ifdef ACPI_OS_INVALID_THREAD_ID +#define ACPI_INVALID_THREAD_ID ACPI_OS_INVALID_THREAD_ID +#else +#define ACPI_INVALID_THREAD_ID ((acpi_thread_id) 0xFFFFFFFF) +#endif + /* Table for the global mutexes */ struct acpi_mutex_info { diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c index 8eef298963a2..258e6157c658 100644 --- a/drivers/acpi/acpica/dbexec.c +++ b/drivers/acpi/acpica/dbexec.c @@ -725,7 +725,8 @@ acpi_db_create_execution_threads(char *num_threads_arg, for (i = 0; i < (num_threads); i++) { status = - acpi_os_execute(OSL_DEBUGGER_THREAD, acpi_db_method_thread, + acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD, + acpi_db_method_thread, &acpi_gbl_db_method_info); if (ACPI_FAILURE(status)) { break; diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c index bef5f4ed22a0..342298a6e10f 100644 --- a/drivers/acpi/acpica/dbxface.c +++ b/drivers/acpi/acpica/dbxface.c @@ -164,6 +164,12 @@ acpi_db_single_step(struct acpi_walk_state * walk_state, ACPI_FUNCTION_ENTRY(); +#ifndef ACPI_APPLICATION + if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) { + return (AE_OK); + } +#endif + /* Check the abort flag */ if (acpi_gbl_abort_method) { @@ -431,7 +437,7 @@ acpi_status acpi_initialize_debugger(void) /* Create the debug execution thread to execute commands */ acpi_gbl_db_threads_terminated = FALSE; - status = acpi_os_execute(OSL_DEBUGGER_THREAD, + status = acpi_os_execute(OSL_DEBUGGER_MAIN_THREAD, acpi_db_execute_thread, NULL); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, @@ -439,6 +445,8 @@ acpi_status acpi_initialize_debugger(void) acpi_gbl_db_threads_terminated = TRUE; return_ACPI_STATUS(status); } + } else { + acpi_gbl_db_thread_id = acpi_os_get_thread_id(); } return_ACPI_STATUS(AE_OK); @@ -485,3 +493,21 @@ void acpi_terminate_debugger(void) } ACPI_EXPORT_SYMBOL(acpi_terminate_debugger) + +/******************************************************************************* + * + * FUNCTION: acpi_set_debugger_thread_id + * + * PARAMETERS: thread_id - Debugger thread ID + * + * RETURN: None + * + * DESCRIPTION: Set debugger thread ID + * + ******************************************************************************/ +void acpi_set_debugger_thread_id(acpi_thread_id thread_id) +{ + acpi_gbl_db_thread_id = thread_id; +} + +ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id) diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index a54ad1cc990c..fbc2baf2b9dc 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -55,7 +55,8 @@ typedef enum { OSL_GLOBAL_LOCK_HANDLER, OSL_NOTIFY_HANDLER, OSL_GPE_HANDLER, - OSL_DEBUGGER_THREAD, + OSL_DEBUGGER_MAIN_THREAD, + OSL_DEBUGGER_EXEC_THREAD, OSL_EC_POLL_HANDLER, OSL_EC_BURST_HANDLER } acpi_execute_type; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index c33eeabde160..f837adf15477 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -939,4 +939,6 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status void **data, void (*callback)(void *))) +void acpi_set_debugger_thread_id(acpi_thread_id thread_id); + #endif /* __ACXFACE_H__ */ -- cgit v1.2.3 From 4d946f7970e51d80f8358e0a619dfb17d89e0920 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:56 +0800 Subject: ACPI: Enable build of AML interpreter debugger This patch enables ACPICA debugger files using a configurable CONFIG_ACPI_DEBUGGER configuration item. Those debugger related code that was originally masked as ACPI_FUTURE_USAGE now gets unmasked. Necessary OSL stubs are also added in this patch: 1. acpi_os_readable(): This should be arch specific in Linux, while this patch doesn't introduce real implementation and a complex mechanism to allow architecture specific acpi_os_readable() to be implemented to validate the address. It may be done by future commits. 2. acpi_os_get_line(): This is used to obtain debugger command input. This patch only introduces a simple KDB concept example in it and the example should be co-working with the code implemented in acpi_os_printf(). Since this KDB example won't be compiled unless ENABLE_DEBUGGER is defined and it seems Linux has already stopped to use ENABLE_DEBUGGER, thus do not expect it can work properly. This patch also cleans up all other ACPI_FUTURE_USAGE surroundings accordingly. 1. Since linkage error can be automatically detected, declaration in the headers needn't be surrounded by ACPI_FUTURE_USAGE. So only the following separate exported fuction bodies are masked by this macro (other exported fucntions may have already been masked at entire module level via drivers/acpi/acpica/Makefile): acpi_install_exception_handler() acpi_subsystem_status() acpi_get_system_info() acpi_get_statistics() acpi_install_initialization_handler() 2. Since strip can automatically zap the no-user functions, functions that are not marked with ACPI_EXPORT_SYMBOL() needn't get surrounded by ACPI_FUTURE_USAGE. So the following function which is not used by Linux kernel now won't get surrounded by this macro: acpi_ps_get_name() Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 9 +++++++++ drivers/acpi/acpica/Makefile | 18 +++++++++++++++++- drivers/acpi/acpica/acinterp.h | 2 -- drivers/acpi/acpica/acnamesp.h | 4 ---- drivers/acpi/acpica/acparser.h | 4 ---- drivers/acpi/acpica/acutils.h | 2 -- drivers/acpi/acpica/evxface.c | 2 +- drivers/acpi/acpica/nsdump.c | 6 ------ drivers/acpi/acpica/pstree.c | 2 -- drivers/acpi/acpica/psutils.c | 2 -- drivers/acpi/acpica/rsutils.c | 2 -- drivers/acpi/acpica/rsxface.c | 4 ++-- drivers/acpi/acpica/utxface.c | 2 +- drivers/acpi/osl.c | 11 +++-------- include/acpi/acpixf.h | 10 ---------- include/acpi/platform/aclinux.h | 7 +++++-- include/acpi/platform/aclinuxex.h | 5 +++++ 17 files changed, 43 insertions(+), 49 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5d1015c26ff4..706c2e95503f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -57,6 +57,15 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT config ACPI_CCA_REQUIRED bool +config ACPI_DEBUGGER + bool "In-kernel debugger (EXPERIMENTAL)" + select ACPI_DEBUG + help + Enable in-kernel debugging facilities: statistics, internal + object dump, single step control method execution. + This is still under development, currently enabling this only + results in the compilation of the ACPICA debugger files. + config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index ac78d7657204..885936f79542 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -177,8 +177,24 @@ acpi-y += \ utxferror.o \ utxfmutex.o -acpi-$(ACPI_FUTURE_USAGE) += \ +acpi-$(CONFIG_ACPI_DEBUGGER) += \ + dbcmds.o \ + dbconvert.o \ + dbdisply.o \ + dbexec.o \ + dbhistry.o \ + dbinput.o \ + dbmethod.o \ + dbnames.o \ + dbobject.o \ + dbstats.o \ + dbutils.o \ + dbxface.o \ rsdump.o \ + +acpi-$(ACPI_FUTURE_USAGE) += \ + dbfileio.o \ + dbtest.o \ utcache.o \ utfileio.o \ utprint.o \ diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index e820ed8f173f..e9e936e78154 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -397,12 +397,10 @@ void acpi_ex_dump_operands(union acpi_operand_object **operands, const char *opcode_name, u32 num_opcodes); -#ifdef ACPI_FUTURE_USAGE void acpi_ex_dump_object_descriptor(union acpi_operand_object *object, u32 flags); void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags); -#endif /* ACPI_FUTURE_USAGE */ /* * exnames - AML namestring support diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index ea0d9076d408..5d261c942a0d 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -193,9 +193,7 @@ acpi_ns_convert_to_resource(union acpi_operand_object *original_object, /* * nsdump - Namespace dump/print utilities */ -#ifdef ACPI_FUTURE_USAGE void acpi_ns_dump_tables(acpi_handle search_base, u32 max_depth); -#endif /* ACPI_FUTURE_USAGE */ void acpi_ns_dump_entry(acpi_handle handle, u32 debug_level); @@ -208,7 +206,6 @@ acpi_status acpi_ns_dump_one_object(acpi_handle obj_handle, u32 level, void *context, void **return_value); -#ifdef ACPI_FUTURE_USAGE void acpi_ns_dump_objects(acpi_object_type type, u8 display_type, @@ -220,7 +217,6 @@ acpi_ns_dump_object_paths(acpi_object_type type, u8 display_type, u32 max_depth, acpi_owner_id owner_id, acpi_handle start_handle); -#endif /* ACPI_FUTURE_USAGE */ /* * nseval - Namespace evaluation functions diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index 6021ccfb0b1c..8fc8c7cea879 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -194,10 +194,8 @@ union acpi_parse_object *acpi_ps_find(union acpi_parse_object *scope, union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn); -#ifdef ACPI_FUTURE_USAGE union acpi_parse_object *acpi_ps_get_depth_next(union acpi_parse_object *origin, union acpi_parse_object *op); -#endif /* ACPI_FUTURE_USAGE */ /* * pswalk - parse tree walk routines @@ -235,9 +233,7 @@ void acpi_ps_free_op(union acpi_parse_object *op); u8 acpi_ps_is_leading_char(u32 c); -#ifdef ACPI_FUTURE_USAGE u32 acpi_ps_get_name(union acpi_parse_object *op); -#endif /* ACPI_FUTURE_USAGE */ void acpi_ps_set_name(union acpi_parse_object *op, u32 name); diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index fb2aa5066f3f..8b8fef6cc32d 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -635,9 +635,7 @@ void acpi_ut_free_and_track(void *address, u32 component, const char *module, u32 line); -#ifdef ACPI_FUTURE_USAGE void acpi_ut_dump_allocation_info(void); -#endif /* ACPI_FUTURE_USAGE */ void acpi_ut_dump_allocations(u32 component, const char *module); diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 81f2d9e87fad..07d22bfbaa00 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -405,7 +405,7 @@ cleanup: } ACPI_EXPORT_SYMBOL(acpi_install_exception_handler) -#endif /* ACPI_FUTURE_USAGE */ +#endif #if (!ACPI_REDUCED_HARDWARE) /******************************************************************************* diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 0f1daba640e7..37aa5c45ca4b 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -60,7 +60,6 @@ acpi_ns_dump_one_device(acpi_handle obj_handle, #if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) -#ifdef ACPI_FUTURE_USAGE static acpi_status acpi_ns_dump_one_object_path(acpi_handle obj_handle, u32 level, void *context, void **return_value); @@ -68,7 +67,6 @@ acpi_ns_dump_one_object_path(acpi_handle obj_handle, static acpi_status acpi_ns_get_max_depth(acpi_handle obj_handle, u32 level, void *context, void **return_value); -#endif /* ACPI_FUTURE_USAGE */ /******************************************************************************* * @@ -625,7 +623,6 @@ cleanup: return (AE_OK); } -#ifdef ACPI_FUTURE_USAGE /******************************************************************************* * * FUNCTION: acpi_ns_dump_objects @@ -680,9 +677,7 @@ acpi_ns_dump_objects(acpi_object_type type, (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); } -#endif /* ACPI_FUTURE_USAGE */ -#ifdef ACPI_FUTURE_USAGE /******************************************************************************* * * FUNCTION: acpi_ns_dump_one_object_path, acpi_ns_get_max_depth @@ -810,7 +805,6 @@ acpi_ns_dump_object_paths(acpi_object_type type, (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); } -#endif /* ACPI_FUTURE_USAGE */ /******************************************************************************* * diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index 89984f30addc..cf2f2faf4f92 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -183,7 +183,6 @@ acpi_ps_append_arg(union acpi_parse_object *op, union acpi_parse_object *arg) } } -#ifdef ACPI_FUTURE_USAGE /******************************************************************************* * * FUNCTION: acpi_ps_get_depth_next @@ -317,4 +316,3 @@ union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op) return (child); } #endif -#endif /* ACPI_FUTURE_USAGE */ diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c index 183cc1efbc51..71d2877cd2ce 100644 --- a/drivers/acpi/acpica/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -205,7 +205,6 @@ u8 acpi_ps_is_leading_char(u32 c) /* * Get op's name (4-byte name segment) or 0 if unnamed */ -#ifdef ACPI_FUTURE_USAGE u32 acpi_ps_get_name(union acpi_parse_object * op) { @@ -219,7 +218,6 @@ u32 acpi_ps_get_name(union acpi_parse_object * op) return (op->named.name); } -#endif /* ACPI_FUTURE_USAGE */ /* * Set op's name diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index 52b024df0052..9486992edbb8 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -564,7 +564,6 @@ acpi_rs_get_crs_method_data(struct acpi_namespace_node *node, * ******************************************************************************/ -#ifdef ACPI_FUTURE_USAGE acpi_status acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, struct acpi_buffer *ret_buffer) @@ -596,7 +595,6 @@ acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, acpi_ut_remove_reference(obj_desc); return_ACPI_STATUS(status); } -#endif /* ACPI_FUTURE_USAGE */ /******************************************************************************* * diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index de51f836ef68..1e8cd5723326 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -220,7 +220,7 @@ acpi_get_current_resources(acpi_handle device_handle, } ACPI_EXPORT_SYMBOL(acpi_get_current_resources) -#ifdef ACPI_FUTURE_USAGE + /******************************************************************************* * * FUNCTION: acpi_get_possible_resources @@ -262,7 +262,7 @@ acpi_get_possible_resources(acpi_handle device_handle, } ACPI_EXPORT_SYMBOL(acpi_get_possible_resources) -#endif /* ACPI_FUTURE_USAGE */ + /******************************************************************************* * * FUNCTION: acpi_set_current_resources diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index a6b0eb034fc0..f9c8f9ce1f0f 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -253,7 +253,7 @@ acpi_install_initialization_handler(acpi_init_handler handler, u32 function) } ACPI_EXPORT_SYMBOL(acpi_install_initialization_handler) -#endif /* ACPI_FUTURE_USAGE */ +#endif /***************************************************************************** * diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 739a4a6b3b9b..327291586f84 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -66,8 +66,6 @@ struct acpi_os_dpc { /* stuff for debugger support */ int acpi_in_debugger; EXPORT_SYMBOL(acpi_in_debugger); - -extern char line_buf[80]; #endif /*ENABLE_DEBUGGER */ static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl, @@ -1345,15 +1343,13 @@ acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units) return AE_OK; } -#ifdef ACPI_FUTURE_USAGE -u32 acpi_os_get_line(char *buffer) +acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read) { - #ifdef ENABLE_DEBUGGER if (acpi_in_debugger) { u32 chars; - kdb_read(buffer, sizeof(line_buf)); + kdb_read(buffer, buffer_length); /* remove the CR kdb includes */ chars = strlen(buffer) - 1; @@ -1361,9 +1357,8 @@ u32 acpi_os_get_line(char *buffer) } #endif - return 0; + return AE_OK; } -#endif /* ACPI_FUTURE_USAGE */ acpi_status acpi_os_signal(u32 function, void *info) { diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index f837adf15477..ded9fa3d7194 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -393,15 +393,11 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_terminate(void)) */ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable(void)) -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_subsystem_status(void)) -#endif -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_system_info(struct acpi_buffer *ret_buffer)) -#endif ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_statistics(struct acpi_statistics *stats)) ACPI_EXTERNAL_RETURN_PTR(const char @@ -625,11 +621,9 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status space_id, acpi_adr_space_handler handler)) -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_install_exception_handler (acpi_exception_handler handler)) -#endif ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_install_interface_handler (acpi_interface_handler handler)) @@ -750,12 +744,10 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_current_resources(acpi_handle device, struct acpi_buffer *ret_buffer)) -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_possible_resources(acpi_handle device, struct acpi_buffer *ret_buffer)) -#endif ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_event_resources(acpi_handle device_handle, struct acpi_buffer @@ -844,7 +836,6 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status /* * ACPI Timer interfaces */ -#ifdef ACPI_FUTURE_USAGE ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_get_timer_resolution(u32 *resolution)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_get_timer(u32 *ticks)) @@ -853,7 +844,6 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 *time_elapsed)) -#endif /* ACPI_FUTURE_USAGE */ /* * Error/Warning output diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 74ba46c8157a..323e5daece54 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -63,12 +63,16 @@ #define ACPI_USE_SYSTEM_INTTYPES -/* Compile for reduced hardware mode only with this kernel config */ +/* Kernel specific ACPICA configuration */ #ifdef CONFIG_ACPI_REDUCED_HARDWARE_ONLY #define ACPI_REDUCED_HARDWARE 1 #endif +#ifdef CONFIG_ACPI_DEBUGGER +#define ACPI_DEBUGGER +#endif + #include #include #include @@ -151,7 +155,6 @@ * OSL interfaces used by utilities */ #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_redirect_output -#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_table_by_name #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_table_by_index #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_table_by_address diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h index acedc3f026de..fd6d70fe1219 100644 --- a/include/acpi/platform/aclinuxex.h +++ b/include/acpi/platform/aclinuxex.h @@ -124,6 +124,11 @@ static inline acpi_thread_id acpi_os_get_thread_id(void) lock ? AE_OK : AE_NO_MEMORY; \ }) +static inline u8 acpi_os_readable(void *pointer, acpi_size length) +{ + return TRUE; +} + /* * OSL interfaces added by Linux */ -- cgit v1.2.3 From aaa93a618b3f1a94f70b09f9e22481d4a51976d0 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:26:02 +0800 Subject: ACPICA: Debugger: Fix dead lock issue ocurred in single stepping mode ACPICA commit 35273add90da19cd8790fdb5735f52e3c9861684 When single step execution is not ended, executing another control methods leads to dead locks around interpreter lock/namespace lock/method serialization lock. So we should only allow one execution from the debugger at same time. Lv Zheng. Link: https://github.com/acpica/acpica/commit/35273add Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/dbexec.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c index 258e6157c658..d713e2df65b9 100644 --- a/drivers/acpi/acpica/dbexec.c +++ b/drivers/acpi/acpica/dbexec.c @@ -370,7 +370,17 @@ acpi_db_execute(char *name, char **args, acpi_object_type * types, u32 flags) #ifdef ACPI_DEBUG_OUTPUT u32 previous_allocations; u32 allocations; +#endif + /* + * Allow one execution to be performed by debugger or single step + * execution will be dead locked by the interpreter mutexes. + */ + if (acpi_gbl_method_executing) { + acpi_os_printf("Only one debugger execution is allowed.\n"); + return; + } +#ifdef ACPI_DEBUG_OUTPUT /* Memory allocation tracking */ previous_allocations = acpi_db_get_outstanding_allocations(); -- cgit v1.2.3 From 49e4b84333f338d4f183f28f1f3c1131b9fb2b5a Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sun, 25 Oct 2015 01:02:19 +0800 Subject: ACPI: Use correct IRQ when uninstalling ACPI interrupt handler Currently when the system is trying to uninstall the ACPI interrupt handler, it uses acpi_gbl_FADT.sci_interrupt as the IRQ number. However, the IRQ number that the ACPI interrupt handled is installed for comes from acpi_gsi_to_irq() and that is the number that should be used for the handler removal. Fix this problem by using the mapped IRQ returned from acpi_gsi_to_irq() as appropriate. Cc: All applicable Acked-by: Lv Zheng Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 9 ++++++--- include/linux/acpi.h | 6 ++++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 739a4a6b3b9b..2a25919f8eab 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -81,6 +81,7 @@ static struct workqueue_struct *kacpid_wq; static struct workqueue_struct *kacpi_notify_wq; static struct workqueue_struct *kacpi_hotplug_wq; static bool acpi_os_initialized; +unsigned int acpi_sci_irq = INVALID_ACPI_IRQ; /* * This list of permanent mappings is for memory that may be accessed from @@ -856,17 +857,19 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_handler = NULL; return AE_NOT_ACQUIRED; } + acpi_sci_irq = irq; return AE_OK; } -acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) +acpi_status acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler handler) { - if (irq != acpi_gbl_FADT.sci_interrupt) + if (gsi != acpi_gbl_FADT.sci_interrupt || !acpi_sci_irq_valid()) return AE_BAD_PARAMETER; - free_irq(irq, acpi_irq); + free_irq(acpi_sci_irq, acpi_irq); acpi_irq_handler = NULL; + acpi_sci_irq = INVALID_ACPI_IRQ; return AE_OK; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 43856d19cf4d..1ae6ba06f9ac 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -193,6 +193,12 @@ int acpi_ioapic_registered(acpi_handle handle, u32 gsi_base); void acpi_irq_stats_init(void); extern u32 acpi_irq_handled; extern u32 acpi_irq_not_handled; +extern unsigned int acpi_sci_irq; +#define INVALID_ACPI_IRQ ((unsigned)-1) +static inline bool acpi_sci_irq_valid(void) +{ + return acpi_sci_irq != INVALID_ACPI_IRQ; +} extern int sbf_port; extern unsigned long acpi_realmode_flags; -- cgit v1.2.3 From efb1cf7d28b8aeacec53e9ba8f3f2809c5cb9686 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sun, 25 Oct 2015 01:02:36 +0800 Subject: ACPI: Using correct irq when waiting for events When the system is waiting for GPE/fixed event handler to finish, it uses acpi_gbl_FADT.sci_interrupt directly as the IRQ number. However, the remapped IRQ returned by acpi_gsi_to_irq() should be passed to synchronize_hardirq() instead of it. Cc: 3.19+ # 3.19+ Acked-by: Lv Zheng Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 2a25919f8eab..2f6e3c6ad39b 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1183,8 +1183,8 @@ void acpi_os_wait_events_complete(void) * Make sure the GPE handler or the fixed event handler is not used * on another CPU after removal. */ - if (acpi_irq_handler) - synchronize_hardirq(acpi_gbl_FADT.sci_interrupt); + if (acpi_sci_irq_valid()) + synchronize_hardirq(acpi_sci_irq); flush_workqueue(kacpid_wq); flush_workqueue(kacpi_notify_wq); } -- cgit v1.2.3 From 8c01275e0cdf1959aa25c322fd5870c097733195 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sun, 25 Oct 2015 01:02:46 +0800 Subject: ACPI / PM: Fix incorrect wakeup IRQ setting during suspend-to-idle For an ACPI compatible system, the SCI (ACPI System Control Interrupt) is used to wake the system up from suspend-to-idle. Once the CPU is woken up by the SCI, the interrupt handler will first check if the current IRQ has been configured for system wakeup, so irq_pm_check_wakeup() is invoked to validate the IRQ number. However, during suspend-to-idle, enable_irq_wake() is called for acpi_gbl_FADT.sci_interrupt, although the IRQ number that the SCI handler has been installed for should be passed to it instead. Thus, if acpi_gbl_FADT.sci_interrupt happens to be different from that number, ACPI interrupts will not be able to wake up the system from sleep. Fix this problem by passing the IRQ number returned by acpi_gsi_to_irq() to enable_irq_wake() instead of acpi_gbl_FADT.sci_interrupt. Cc: 3.18+ # 3.18+ Acked-by: Lv Zheng Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2f0d4db40a9e..3fe1fbec7677 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -632,14 +632,16 @@ static int acpi_freeze_prepare(void) acpi_enable_wakeup_devices(ACPI_STATE_S0); acpi_enable_all_wakeup_gpes(); acpi_os_wait_events_complete(); - enable_irq_wake(acpi_gbl_FADT.sci_interrupt); + if (acpi_sci_irq_valid()) + enable_irq_wake(acpi_sci_irq); return 0; } static void acpi_freeze_restore(void) { acpi_disable_wakeup_devices(ACPI_STATE_S0); - disable_irq_wake(acpi_gbl_FADT.sci_interrupt); + if (acpi_sci_irq_valid()) + disable_irq_wake(acpi_sci_irq); acpi_enable_all_runtime_gpes(); } -- cgit v1.2.3 From 21657471688508ced523b3bf0a763d73d546ad1a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 26 Oct 2015 04:02:01 +0100 Subject: ACPI / PAD: power_saving_thread() is not freezable power_saving_thread() calls try_to_freeze(), but the thread doesn't mark itself freezable through set_freezable(), so the try_to_freeze() call is useless. Signed-off-by: Jiri Kosina Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_pad.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index ae307ff36acb..8ea8211b2d58 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -148,8 +148,6 @@ static int power_saving_thread(void *data) while (!kthread_should_stop()) { unsigned long expire_time; - try_to_freeze(); - /* round robin to cpus */ expire_time = last_jiffies + round_robin_time * HZ; if (time_before(expire_time, jiffies)) { -- cgit v1.2.3 From 32c0b2f606a4b71fc7b7cfa479cf47648854d9d9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 22 Oct 2015 22:52:59 +0300 Subject: ACPI / CPPC: signedness bug in register_pcc_channel() The "pcc_subspace_idx" is -1 if it hasn't been initialized yet. We need it to be signed. Fixes: 337aadff8e45 (ACPI: Introduce CPU performance controls using CPPC) Signed-off-by: Dan Carpenter Acked-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 0bbf84bdcdc4..2ed55161bca5 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -302,7 +302,7 @@ err_ret: } EXPORT_SYMBOL_GPL(acpi_get_psd_map); -static int register_pcc_channel(unsigned pcc_subspace_idx) +static int register_pcc_channel(int pcc_subspace_idx) { struct acpi_pcct_subspace *cppc_ss; unsigned int len; -- cgit v1.2.3 From 4219853aef645d29b122daa8a8b7bd792f1cc7c9 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Fri, 23 Oct 2015 05:02:52 -0400 Subject: ACPI / CPPC: Fix potential memory leak Commit 337aadff8e45 (ACPI: Introduce CPU performance controls using CPPC) leads to the following static checker warning: drivers/acpi/cppc_acpi.c:527 acpi_cppc_processor_probe() warn: overwrite may leak 'cpc_ptr' Fix the warning by removing the bogus per-CPU pointer dereference. Fixes: 337aadff8e45 (ACPI: Introduce CPU performance controls using CPPC) Reported-by: Dan Carpenter Signed-off-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 2ed55161bca5..3c083d2cc434 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -524,7 +524,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) return 0; out_free: - cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); kfree(cpc_ptr); out_buf_free: -- cgit v1.2.3 From 584d8d1eb123b8be1274bf69f3ce07cee848d40d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 21 Oct 2015 13:45:03 +0200 Subject: ACPI / video: Add a quirk to force native backlight on Lenovo IdeaPad S405 The Lenovo IdeaPad S405 is a not "Windows8 ready" machine which still has a broken ACPI video backlight implementation. Add a quirk to force use of native backlight on this machine. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1201530 Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/video_detect.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 2922f1f252d5..0d3a384b508a 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -243,6 +243,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }, /* Non win8 machines which need native backlight nevertheless */ + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1201530 */ + .callback = video_detect_force_native, + .ident = "Lenovo Ideapad S405", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "Lenovo IdeaPad S405"), + }, + }, { /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ .callback = video_detect_force_native, -- cgit v1.2.3 From bc1e49df345cf9be38ba29ead45a12f86ceb5fe2 Mon Sep 17 00:00:00 2001 From: Insu Yun Date: Thu, 15 Oct 2015 12:19:30 -0400 Subject: ACPI / sysfs: correctly check failing memory allocation Since kobject_create_and_add() can fail under memory pressure, its return value needs to be checked against NULL before passing it to sysfs_create_file(). Signed-off-by: Insu Yun [ rjw: Subject & changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sysfs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 40a42655227c..0243d375c6fd 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -878,6 +878,9 @@ int __init acpi_sysfs_init(void) return result; hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); + if (!hotplug_kobj) + return -ENOMEM; + result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr); if (result) return result; -- cgit v1.2.3