summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpica/nsxfname.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/acpica/nsxfname.c')
-rw-r--r--drivers/acpi/acpica/nsxfname.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
index 9589fea24997..f23593d6add4 100644
--- a/drivers/acpi/acpica/nsxfname.c
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -45,6 +45,8 @@
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
+#include "acparser.h"
+#include "amlcode.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsxfname")
@@ -358,3 +360,151 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer)
}
ACPI_EXPORT_SYMBOL(acpi_get_object_info)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_install_method
+ *
+ * PARAMETERS: Buffer - An ACPI table containing one control method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a control method into the namespace. If the method
+ * name already exists in the namespace, it is overwritten. The
+ * input buffer must contain a valid DSDT or SSDT containing a
+ * single control method.
+ *
+ ******************************************************************************/
+acpi_status acpi_install_method(u8 *buffer)
+{
+ struct acpi_table_header *table =
+ ACPI_CAST_PTR(struct acpi_table_header, buffer);
+ u8 *aml_buffer;
+ u8 *aml_start;
+ char *path;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *method_obj;
+ struct acpi_parse_state parser_state;
+ u32 aml_length;
+ u16 opcode;
+ u8 method_flags;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!buffer) {
+ return AE_BAD_PARAMETER;
+ }
+
+ /* Table must be a DSDT or SSDT */
+
+ if (!ACPI_COMPARE_NAME(table->signature, ACPI_SIG_DSDT) &&
+ !ACPI_COMPARE_NAME(table->signature, ACPI_SIG_SSDT)) {
+ return AE_BAD_HEADER;
+ }
+
+ /* First AML opcode in the table must be a control method */
+
+ parser_state.aml = buffer + sizeof(struct acpi_table_header);
+ opcode = acpi_ps_peek_opcode(&parser_state);
+ if (opcode != AML_METHOD_OP) {
+ return AE_BAD_PARAMETER;
+ }
+
+ /* Extract method information from the raw AML */
+
+ parser_state.aml += acpi_ps_get_opcode_size(opcode);
+ parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state);
+ path = acpi_ps_get_next_namestring(&parser_state);
+ method_flags = *parser_state.aml++;
+ aml_start = parser_state.aml;
+ aml_length = ACPI_PTR_DIFF(parser_state.pkg_end, aml_start);
+
+ /*
+ * Allocate resources up-front. We don't want to have to delete a new
+ * node from the namespace if we cannot allocate memory.
+ */
+ aml_buffer = ACPI_ALLOCATE(aml_length);
+ if (!aml_buffer) {
+ return AE_NO_MEMORY;
+ }
+
+ method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
+ if (!method_obj) {
+ ACPI_FREE(aml_buffer);
+ return AE_NO_MEMORY;
+ }
+
+ /* Lock namespace for acpi_ns_lookup, we may be creating a new node */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* The lookup either returns an existing node or creates a new one */
+
+ status =
+ acpi_ns_lookup(NULL, path, ACPI_TYPE_METHOD, ACPI_IMODE_LOAD_PASS1,
+ ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND,
+ NULL, &node);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ if (ACPI_FAILURE(status)) { /* ns_lookup */
+ if (status != AE_ALREADY_EXISTS) {
+ goto error_exit;
+ }
+
+ /* Node existed previously, make sure it is a method node */
+
+ if (node->type != ACPI_TYPE_METHOD) {
+ status = AE_TYPE;
+ goto error_exit;
+ }
+ }
+
+ /* Copy the method AML to the local buffer */
+
+ ACPI_MEMCPY(aml_buffer, aml_start, aml_length);
+
+ /* Initialize the method object with the new method's information */
+
+ method_obj->method.aml_start = aml_buffer;
+ method_obj->method.aml_length = aml_length;
+
+ method_obj->method.param_count = (u8)
+ (method_flags & AML_METHOD_ARG_COUNT);
+
+ method_obj->method.method_flags = (u8)
+ (method_flags & ~AML_METHOD_ARG_COUNT);
+
+ if (method_flags & AML_METHOD_SERIALIZED) {
+ method_obj->method.sync_level = (u8)
+ ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4);
+ }
+
+ /*
+ * Now that it is complete, we can attach the new method object to
+ * the method Node (detaches/deletes any existing object)
+ */
+ status = acpi_ns_attach_object(node, method_obj, ACPI_TYPE_METHOD);
+
+ /*
+ * Flag indicates AML buffer is dynamic, must be deleted later.
+ * Must be set only after attach above.
+ */
+ node->flags |= ANOBJ_ALLOCATED_BUFFER;
+
+ /* Remove local reference to the method object */
+
+ acpi_ut_remove_reference(method_obj);
+ return status;
+
+error_exit:
+
+ ACPI_FREE(aml_buffer);
+ ACPI_FREE(method_obj);
+ return status;
+}
+ACPI_EXPORT_SYMBOL(acpi_install_method)