2015-11-06 06:46:57

by Zheng, Lv

[permalink] [raw]
Subject: [RFC PATCH v2 0/5] ACPICA / debugger: Add in-kernel AML debugger support

This patchset enables ACPICA debugger for Linux kernel and implements a
userspace utility to access it.

A. Build the AML debugger
In order to build the kernel support of AML debugger, the following kconfig
items should be enabled:
CONFIG_ACPI_DEBUG=y
CONFIG_ACPI_DEBUGGER=y
CONFIG_DEBUG_FS=y
CONFIG_ACPI_DEBUGGER_USER=m
The userspace tool can be found at tools/power/acpi/tools/acpidbg. To
build this utility, staying in tools folder and type "make acpi".

B. Load the AML debugger during runtime
In order to use the in-kernel AML debugger, the following command should be
executed using root user:
# modprobe acpi_dbg
# mount -t debugfs none /sys/kernel/debug
# acpidbg

C. Batch mode
In order to support scripts, the userspace utility also supports single
command batch mode:
# acpidbg -b "help"
# acpidbg -b "tables"
# acpidbg -b "find _LID"
# acpidbg -b "execute \_SB.LID0._LID"
You can find the documentation about the ACPICA debugger commands in:
https://acpica.org/sites/acpica/files/acpica-reference_17.pdf
(The latest document can be found at https://acpica.org/documentation)
And refer to the chapter - ACPICA debugger reference to obtain the full
description of the debugger commands. Note that not all commands are
supported by an in-kernel AML debugger.

D. Unload the AML debugger during runtime
After terminating all acpidbg instances, the following command can be
executed to remove the AML debugger from kernel:
# rmmod acpi_dbg

The following tasks are not completed:
1. .flush() support in the kernel debugger IO driver.
2. multi-commands batch mode.
3. upstream the userspace acpidbg to the ACPICA upstream.

Colin Ian King (1):
ACPICA: Debugger: Remove unnecessary status check

Lv Zheng (4):
ACPICA: Debugger: Convert some mechanisms to OSPM specific
ACPI / debugger: Add IO interface to access debugger functionalities
tools/power/acpi: Add userspace AML interface support
ACPI / debugger: Add module support for ACPI debugger

drivers/acpi/Kconfig | 13 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 818 ++++++++++++++++++++++++++++++
drivers/acpi/acpica/acdebug.h | 2 +-
drivers/acpi/acpica/acglobal.h | 5 -
drivers/acpi/acpica/dbinput.c | 100 +---
drivers/acpi/acpica/dbxface.c | 63 +--
drivers/acpi/acpica/utmutex.c | 17 -
drivers/acpi/bus.c | 1 +
drivers/acpi/osl.c | 250 ++++++++-
include/acpi/acpiosxf.h | 18 +-
include/acpi/acpixf.h | 11 +
include/acpi/platform/aclinux.h | 2 +
include/acpi/platform/aclinuxex.h | 9 +
include/linux/acpi.h | 71 +++
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 +
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++
18 files changed, 1699 insertions(+), 163 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

--
1.7.10


2015-11-06 06:47:07

by Zheng, Lv

[permalink] [raw]
Subject: [RFC PATCH v2 1/5] ACPICA: Debugger: Remove unnecessary status check

From: Colin Ian King <[email protected]>

ACPICA commit f9d5c6c9a25e9f5ac05458bfcd8b381e21bb2ba5

ACPICA BZ 1205. Colin Ian King.

Link: https://bugs.acpica.org/show_bug.cgi?id=1205
Link: https://github.com/acpica/acpica/commit/f9d5c6c9
Signed-off-by: Colin Ian King <[email protected]>
Signed-off-by: Bob Moore <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/dbinput.c | 3 ---
1 file changed, 3 deletions(-)

diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index 0480254..fe93f67 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -1246,9 +1246,6 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
* 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,
--
1.7.10

2015-11-06 06:47:14

by Zheng, Lv

[permalink] [raw]
Subject: [RFC PATCH v2 2/5] ACPICA: Debugger: Convert some mechanisms to OSPM specific

The following mechanisms are OSPM specific:
1. Redirect output destination to console: no file redirection will be
needed by an in-kernel debugger, there is even no file can be accessed
when the debugger is running in the kernel mode.
2. Output command prompts: programs other than acpiexec can have different
prompt characters and the prompt characters may be implemented as a
special character sequence to form a char device IO protocol.
3. Command ready/complete handshake: OSPM debugger may wait more conditions
to implement OSPM specific semantics (for example, FIFO full/empty
conditions for O_NONBLOCK or IO open/close conditions).
Leaving such OSPM specific stuffs in the ACPICA debugger core blocks
Linux debugger IO driver implementation.

Several new OSL APIs are provided by this patch:
1. acpi_os_initialize_command_signals: initialize command handshake mechanism
or any other OSPM specific stuffs.
2. acpi_os_terminate_command_signals: reversal of
acpi_os_initialize_command_signals.
3. acpi_os_wait_command_ready: putting debugger task into wait state when a
command is not ready. OSPMs can terminate command loop by returning
AE_CTRL_TERMINATE from this API. Normally, wait_event() or
wait_for_multiple_object() may be used to implement this API.
4. acpi_os_notify_command_complete: putting user task into running state when a
command has been completed. OSPMs can terminate command loop by
returning AE_CTRL_TERMINATE from this API. Normally, wake_up() or
set_event() may be used to implement this API.
This patch also converts current command signaling implementation into a
generic debugger layer (osgendbg.c) to be used by the existing OSPMs or
acpiexec, in return, Linux can have chance to implement its own command
handshake mechanism. This patch also implements acpiexec batch mode in a
multi-threading mode comaptible style as a demo (this can be confirmed by
configuring acpiexec into DEBUGGER_MULTI_THREADED mode where the batch mode
is still working). Lv Zheng.

Note that the OSPM specific command handshake mechanism is required by
Linux kernel because:
1. Linux kernel trends to use wait queue to synchronize two threads, using
mutexes to achieve that will cause false "dead lock" warnings.
2. The command handshake mechanism implemented by ACPICA is implemented in
this way because of a design issue in debugger IO streaming. Debugger IO
outputs are simply cached using a giant buffer, this should be tuned by
Linux in the future.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/acdebug.h | 2 +-
drivers/acpi/acpica/acglobal.h | 5 --
drivers/acpi/acpica/dbinput.c | 97 ++++++++-----------------------------
drivers/acpi/acpica/dbxface.c | 63 +++++++-----------------
drivers/acpi/acpica/utmutex.c | 17 -------
include/acpi/acpiosxf.h | 18 ++++++-
include/acpi/acpixf.h | 11 +++++
include/acpi/platform/aclinux.h | 4 ++
include/acpi/platform/aclinuxex.h | 19 ++++++++
9 files changed, 89 insertions(+), 147 deletions(-)

diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index c928ba4..86474d8 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -257,7 +257,7 @@ acpi_db_command_dispatch(char *input_buffer,

void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context);

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op);
+acpi_status acpi_db_user_commands(void);

char *acpi_db_get_next_token(char *string,
char **next, acpi_object_type * return_type);
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index faa9760..3977134 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -326,7 +326,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list);
#ifdef ACPI_DEBUGGER

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);
@@ -345,7 +344,6 @@ ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]);

/* These buffers should all be the same size */

-ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]);
@@ -360,9 +358,6 @@ 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/dbinput.c b/drivers/acpi/acpica/dbinput.c
index fe93f67..2bf8e6b 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -53,8 +53,6 @@ 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);
@@ -1149,55 +1147,16 @@ acpi_db_command_dispatch(char *input_buffer,

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_db_terminate_loop) {
- 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);
- }
+ (void)acpi_db_user_commands();
acpi_gbl_db_threads_terminated = TRUE;
}

/*******************************************************************************
*
- * 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
+ * PARAMETERS: None
*
* RETURN: None
*
@@ -1206,7 +1165,7 @@ static void acpi_db_single_thread(void)
*
******************************************************************************/

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
+acpi_status acpi_db_user_commands(void)
{
acpi_status status = AE_OK;

@@ -1216,49 +1175,31 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)

while (!acpi_gbl_db_terminate_loop) {

- /* 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 */
+ /* Wait the readiness of the command */

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_wait_command_ready();
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "While parsing command line"));
- return (status);
+ break;
}

- /* Check for single or multithreaded debug */
+ /* Just call to the command line interpreter */

- 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);
+ acpi_gbl_method_executing = FALSE;
+ acpi_gbl_step_to_next_call = FALSE;

- 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 */
+ (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL,
+ NULL);
+
+ /* Notify the completion of the command */

- acpi_db_single_thread();
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ break;
}
}

+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status, "While parsing command line"));
+ }
return (status);
}
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index 342298a..d95e91f 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -85,46 +85,21 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

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);
+ while (status == AE_CTRL_TRUE) {

- /* Different prompt if method is executing */
+ /* Notify the completion of the command */

- if (!acpi_gbl_method_executing) {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_COMMAND_PROMPT);
- } else {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }

- /* Get the user input line */
+ /* Wait the readiness of the command */

- 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_os_wait_command_ready();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
}

status =
@@ -134,6 +109,11 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */

+error_exit:
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While parsing/handling command line"));
+ }
return (status);
}

@@ -420,15 +400,7 @@ acpi_status acpi_initialize_debugger(void)

/* 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);
+ status = acpi_os_initialize_command_signals();
if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get debugger mutex\n");
return_ACPI_STATUS(status);
@@ -473,13 +445,14 @@ void acpi_terminate_debugger(void)
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);
}
+
+ acpi_os_terminate_command_signals();
}

if (acpi_gbl_db_buffer) {
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index ce406e3..ea0c207 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -111,17 +111,6 @@ acpi_status acpi_ut_mutex_initialize(void)
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);
}
@@ -162,12 +151,6 @@ 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/acpiosxf.h b/include/acpi/acpiosxf.h
index fbc2baf..0d824a2 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -349,12 +349,28 @@ void acpi_os_redirect_output(void *destination);
#endif

/*
- * Debug input
+ * Debug IO
*/
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line
acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read);
#endif

+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+acpi_status acpi_os_initialize_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+void acpi_os_terminate_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+acpi_status acpi_os_wait_command_ready(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete
+acpi_status acpi_os_notify_command_complete(void);
+#endif
+
/*
* Obtain ACPI table(s)
*/
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 3aaaa86..95ebae3 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -264,6 +264,15 @@ ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0);

/*
+ * Debugger command handshake globals. Host OSes need to access these
+ * variables to implement their own command handshake mechanism.
+ */
+#ifdef ACPI_DEBUGGER
+ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE);
+ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
+#endif
+
+/*
* Other miscellaneous globals
*/
ACPI_GLOBAL(struct acpi_table_fadt, acpi_gbl_FADT);
@@ -929,6 +938,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
void **data,
void (*callback)(void *)))

+void acpi_run_debugger(char *batch_buffer);
+
void acpi_set_debugger_thread_id(acpi_thread_id thread_id);

#endif /* __ACXFACE_H__ */
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 323e5da..69dbae6 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -150,6 +150,10 @@
*/
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index fd6d70f..673fdf4 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -129,6 +129,25 @@ static inline u8 acpi_os_readable(void *pointer, acpi_size length)
return TRUE;
}

+static inline acpi_status acpi_os_initialize_command_signals(void)
+{
+ return AE_OK;
+}
+
+static inline void acpi_os_terminate_command_signals(void)
+{
+}
+
+static inline acpi_status acpi_os_wait_command_ready(void)
+{
+ return AE_ERROR;
+}
+
+static inline acpi_status acpi_os_notify_command_complete(void)
+{
+ return AE_ERROR;
+}
+
/*
* OSL interfaces added by Linux
*/
--
1.7.10

2015-11-06 06:47:21

by Zheng, Lv

[permalink] [raw]
Subject: [RFC PATCH v2 3/5] ACPI / debugger: Add IO interface to access debugger functionalities

This patch adds /sys/kernel/debug/acpi/acpidbg, which can be used by
userspace programs to access ACPICA debugger functionalities.

Known issue:
1. IO flush support
acpi_os_notify_command_complete() and acpi_os_wait_command_ready() can
be used by acpi_dbg module to implement .flush() filesystem operation.
While this patch doesn't go that far. It then becomes userspace tool's
duty now to flush old commands before executing new batch mode commands.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 2 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 792 +++++++++++++++++++++++++++++++++++++
drivers/acpi/bus.c | 2 +
drivers/acpi/osl.c | 55 ++-
include/acpi/platform/aclinux.h | 2 -
include/acpi/platform/aclinuxex.h | 10 -
include/linux/acpi_dbg.h | 52 +++
8 files changed, 899 insertions(+), 17 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 25dbb76..2b89fd7 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -58,7 +58,7 @@ config ACPI_CCA_REQUIRED
bool

config ACPI_DEBUGGER
- bool "In-kernel debugger (EXPERIMENTAL)"
+ bool "In-kernel debugger"
select ACPI_DEBUG
help
Enable in-kernel debugging facilities: statistics, internal
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..102b5e6 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,6 +50,7 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
+acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
new file mode 100644
index 0000000..853ea94
--- /dev/null
+++ b/drivers/acpi/acpi_dbg.c
@@ -0,0 +1,792 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI : AML: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/acpi_dbg.h>
+#include "internal.h"
+
+#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
+#define ACPI_AML_BUF_SIZE PAGE_SIZE
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define ACPI_AML_OPENED 0x0001
+#define ACPI_AML_CLOSED 0x0002
+#define ACPI_AML_IN_USER 0x0004 /* user space is writing cmd */
+#define ACPI_AML_IN_KERN 0x0008 /* kernel space is reading cmd */
+#define ACPI_AML_OUT_USER 0x0010 /* user space is reading log */
+#define ACPI_AML_OUT_KERN 0x0020 /* kernel space is writing log */
+#define ACPI_AML_USER (ACPI_AML_IN_USER | ACPI_AML_OUT_USER)
+#define ACPI_AML_KERN (ACPI_AML_IN_KERN | ACPI_AML_OUT_KERN)
+#define ACPI_AML_BUSY (ACPI_AML_USER | ACPI_AML_KERN)
+#define ACPI_AML_OPEN (ACPI_AML_OPENED | ACPI_AML_CLOSED)
+
+struct acpi_aml_io {
+ wait_queue_head_t wait;
+ unsigned long flags;
+ unsigned long users;
+ struct mutex lock;
+ struct task_struct *thread;
+ char out_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf out_crc;
+ char in_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf in_crc;
+ acpi_osd_exec_callback function;
+ void *context;
+ unsigned long usages;
+};
+
+static struct acpi_aml_io acpi_aml_io;
+static bool acpi_aml_initialized;
+static struct file *acpi_aml_active_reader;
+static struct dentry *acpi_aml_dentry;
+
+static inline bool __acpi_aml_running(void)
+{
+ return acpi_aml_io.thread ? true : false;
+}
+
+static inline bool __acpi_aml_access_ok(unsigned long flag)
+{
+ /*
+ * The debugger interface is in opened state (OPENED && !CLOSED),
+ * then it is allowed to access the debugger buffers from either
+ * user space or the kernel space.
+ * In addition, for the kernel space, only the debugger thread
+ * (thread ID matched) is allowed to access.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED) ||
+ (acpi_aml_io.flags & ACPI_AML_CLOSED) ||
+ !__acpi_aml_running())
+ return false;
+ if ((flag & ACPI_AML_KERN) &&
+ current != acpi_aml_io.thread)
+ return false;
+ return true;
+}
+
+static inline bool __acpi_aml_readable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another read is not in progress and there is data in buffer
+ * available for read.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_count(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_writable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another write is not in progress and there is buffer space
+ * available for write.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_space(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_busy(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_BUSY)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_opened(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_OPEN)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_used(void)
+{
+ return acpi_aml_io.usages ? true : false;
+}
+
+static inline bool acpi_aml_running(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_running();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_busy(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_busy();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_used(void)
+{
+ bool ret;
+
+ /*
+ * The usage count is prepared to avoid race conditions between the
+ * starts and the stops of the debugger thread.
+ */
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_used();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_KERN) ||
+ __acpi_aml_readable(&acpi_aml_io.in_crc, ACPI_AML_IN_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_KERN) ||
+ __acpi_aml_writable(&acpi_aml_io.out_crc, ACPI_AML_OUT_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_USER) ||
+ __acpi_aml_readable(&acpi_aml_io.out_crc, ACPI_AML_OUT_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_USER) ||
+ __acpi_aml_writable(&acpi_aml_io.in_crc, ACPI_AML_IN_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_write(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_writable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_read(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_readable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static void acpi_aml_unlock_fifo(unsigned long flag, bool wakeup)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~flag;
+ if (wakeup)
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+}
+
+static int acpi_aml_write_kern(const char *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting logs */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ memcpy(p, buf, n);
+ /* sync head after inserting logs */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_KERN, true);
+ return n;
+}
+
+static int acpi_aml_readb_kern(void)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing cmds */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ ret = (int)*p;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ crc->tail = (crc->tail + 1) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_IN_KERN, true);
+ return ret;
+}
+
+/*
+ * acpi_aml_write_log() - Capture debugger output
+ * @msg: the debugger output
+ *
+ * This function should be used to implement acpi_os_printf() to filter out
+ * the debugger output and store the output into the debugger interface
+ * buffer. Return the size of stored logs or errno.
+ */
+ssize_t acpi_aml_write_log(const char *msg)
+{
+ int ret = 0;
+ int count = 0, size = 0;
+
+ if (!acpi_aml_initialized)
+ return -ENODEV;
+ if (msg)
+ count = strlen(msg);
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_kern(msg + size, count);
+ /*
+ * A control method execution may be blocked if we wait
+ * user space here and some kernel functionalities may be
+ * executing another control method that need to be
+ * synchronized with the one executed by the debugger
+ * thread. Thus we do not wait when the user space isn't
+ * fast enough to drain the logs, so that the logs are
+ * dropped when the buffer is overflow and the normal
+ * kernel functionalities won't get blocked.
+ *
+ * A module parameter may be used to tune this if the
+ * integrity of the debugger output is required.
+ */
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ break;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ size += ret;
+ count -= ret;
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_write_log);
+
+/*
+ * acpi_aml_read_cmd() - Capture debugger input
+ * @msg: the debugger input
+ * @size: the size of the debugger input
+ *
+ * This function should be used to implement acpi_os_get_line() to capture
+ * the debugger input commands and store the input commands into the
+ * debugger interface buffer. Return the size of stored commands or errno.
+ */
+ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+{
+ int ret = 0;
+ int size = 0;
+
+ /*
+ * This is ensured by the running fact of the debugger thread
+ * unless a bug is introduced.
+ */
+ BUG_ON(!acpi_aml_initialized);
+ while (count > 0) {
+again:
+ /*
+ * Check each input byte to find the end of the command.
+ */
+ ret = acpi_aml_readb_kern();
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_readable());
+ /*
+ * We need to retry when the condition becomes
+ * true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ *(msg + size) = (char)ret;
+ size++;
+ count--;
+ if (ret == '\n') {
+ /*
+ * acpi_os_get_line() requires a zero terminated command
+ * string.
+ */
+ *(msg + size - 1) = '\0';
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_read_cmd);
+
+static int acpi_aml_thread(void *unsed)
+{
+ acpi_osd_exec_callback function = NULL;
+ void *context;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (acpi_aml_io.function) {
+ acpi_aml_io.usages++;
+ function = acpi_aml_io.function;
+ context = acpi_aml_io.context;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ if (function)
+ function(context);
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.usages--;
+ if (!__acpi_aml_used()) {
+ acpi_aml_io.thread = NULL;
+ wake_up(&acpi_aml_io.wait);
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ return 0;
+}
+
+/*
+ * acpi_aml_create_thread() - Create AML debugger thread
+ * @function: the debugger thread callback
+ * @context: the context to be passed to the debugger thread
+ *
+ * This function should be used to implement acpi_os_execute() which is
+ * used by the ACPICA debugger to create the debugger thread.
+ */
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ struct task_struct *t;
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.function = function;
+ acpi_aml_io.context = context;
+ mutex_unlock(&acpi_aml_io.lock);
+
+ t = kthread_create(acpi_aml_thread, NULL, "aml");
+ if (IS_ERR(t)) {
+ pr_err("Failed to create AML debugger thread.\n");
+ return PTR_ERR(t);
+ }
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.thread = t;
+ acpi_set_debugger_thread_id((acpi_thread_id)(unsigned long)t);
+ wake_up_process(t);
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_create_thread);
+
+int acpi_aml_wait_command_ready(void)
+{
+ acpi_status status;
+
+ if (!acpi_gbl_method_executing)
+ acpi_os_printf("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
+ else
+ acpi_os_printf("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+
+ status = acpi_os_get_line(acpi_gbl_db_line_buf,
+ ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_wait_command_ready);
+
+int acpi_aml_notify_command_complete(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_notify_command_complete);
+
+static int acpi_aml_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ acpi_status status;
+
+ mutex_lock(&acpi_aml_io.lock);
+ /*
+ * The debugger interface is being closed, no new user is allowed
+ * during this period.
+ */
+ if (acpi_aml_io.flags & ACPI_AML_CLOSED) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ /*
+ * Only one reader is allowed to initiate the debugger
+ * thread.
+ */
+ if (acpi_aml_active_reader) {
+ ret = -EBUSY;
+ goto err_lock;
+ } else {
+ pr_debug("Opening debugger reader.\n");
+ acpi_aml_active_reader = file;
+ }
+ } else {
+ /*
+ * No writer is allowed unless the debugger thread is
+ * ready.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ }
+ if (acpi_aml_active_reader == file) {
+ pr_debug("Opening debugger interface.\n");
+ mutex_unlock(&acpi_aml_io.lock);
+
+ pr_debug("Initializing debugger thread.\n");
+ status = acpi_initialize_debugger();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Failed to initialize debugger.\n");
+ ret = -EINVAL;
+ goto err_lock;
+ }
+ acpi_aml_io.flags |= ACPI_AML_OPENED;
+ pr_debug("Debugger thread initialized.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0;
+ acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0;
+ pr_debug("Debugger interface opened.\n");
+ }
+ acpi_aml_io.users++;
+err_lock:
+ if (IS_ERR_VALUE(ret)) {
+ if (acpi_aml_active_reader == file)
+ acpi_aml_active_reader = NULL;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.users--;
+ if (file == acpi_aml_active_reader) {
+ pr_debug("Closing debugger reader.\n");
+ acpi_aml_active_reader = NULL;
+
+ pr_debug("Closing debugger interface.\n");
+ acpi_aml_io.flags |= ACPI_AML_CLOSED;
+
+ /*
+ * Wake up all user space/kernel space blocked
+ * readers/writers.
+ */
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+ /*
+ * Wait all user space/kernel space readers/writers to
+ * stop so that ACPICA command loop of the debugger thread
+ * should fail all its command line reads after this point.
+ */
+ wait_event(acpi_aml_io.wait, !acpi_aml_busy());
+
+ /*
+ * Then we try to terminate the debugger thread if it is
+ * not terminated.
+ */
+ pr_debug("Terminating debugger thread.\n");
+ acpi_terminate_debugger();
+ wait_event(acpi_aml_io.wait, !acpi_aml_used());
+ pr_debug("Debugger thread terminated.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~ACPI_AML_OPENED;
+ }
+ if (acpi_aml_io.users == 0) {
+ pr_debug("Debugger interface closed.\n");
+ acpi_aml_io.flags &= ~ACPI_AML_CLOSED;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_read_user(char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing logs */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ n = min(len, circ_count_to_end(crc));
+ ret = copy_to_user(buf, p, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync tail after removing logs */
+ smp_mb();
+ crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !IS_ERR_VALUE(ret));
+ return ret;
+}
+
+static ssize_t acpi_aml_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_read_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_readable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_write_user(const char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ ret = copy_from_user(p, buf, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync head after inserting cmds */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !IS_ERR_VALUE(ret));
+ return n;
+}
+
+static ssize_t acpi_aml_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static unsigned int acpi_aml_poll(struct file *file, poll_table *wait)
+{
+ int masks = 0;
+
+ poll_wait(file, &acpi_aml_io.wait, wait);
+ if (acpi_aml_user_readable())
+ masks |= POLLIN | POLLRDNORM;
+ if (acpi_aml_user_writable())
+ masks |= POLLOUT | POLLWRNORM;
+
+ return masks;
+}
+
+static const struct file_operations acpi_aml_operations = {
+ .read = acpi_aml_read,
+ .write = acpi_aml_write,
+ .poll = acpi_aml_poll,
+ .open = acpi_aml_open,
+ .release = acpi_aml_release,
+ .llseek = generic_file_llseek,
+};
+
+int __init acpi_aml_init(void)
+{
+ if (!acpi_debugfs_dir)
+ return -ENOENT;
+ /* Initialize AML IO interface */
+ mutex_init(&acpi_aml_io.lock);
+ init_waitqueue_head(&acpi_aml_io.wait);
+ acpi_aml_io.out_crc.buf = acpi_aml_io.out_buf;
+ acpi_aml_io.in_crc.buf = acpi_aml_io.in_buf;
+ acpi_aml_dentry = debugfs_create_file("acpidbg",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ acpi_debugfs_dir, NULL,
+ &acpi_aml_operations);
+ if (acpi_aml_dentry == NULL)
+ return -ENODEV;
+ acpi_aml_initialized = true;
+ return 0;
+}
+
+#if 0
+void __exit acpi_aml_exit(void)
+{
+ /* TODO: Stop the in kernel debugger */
+ if (acpi_aml_dentry)
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_initialized = false;
+}
+
+module_init(acpi_aml_init);
+module_exit(acpi_aml_exit);
+#endif
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a212cef..06fbba9 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,6 +37,7 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
+#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1094,6 +1095,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
+ acpi_aml_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 3272915..2de8f66 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,6 +40,7 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
+#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -233,7 +234,8 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- printk(KERN_CONT "%s", buffer);
+ if (acpi_aml_write_log(buffer) < 0)
+ printk(KERN_CONT "%s", buffer);
#endif
}

@@ -1124,6 +1126,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));

+ if (type == OSL_DEBUGGER_MAIN_THREAD) {
+ ret = acpi_aml_create_thread(function, context);
+ if (ret) {
+ pr_err("Call to kthread_create() failed.\n");
+ status = AE_ERROR;
+ }
+ goto out_thread;
+ }
+
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the work_struct list in a
@@ -1148,11 +1159,17 @@ acpi_status acpi_os_execute(acpi_execute_type type,
if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else {
+ } else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ } else {
+ pr_err("Unsupported os_execute type %d.\n", type);
+ status = AE_ERROR;
}

+ if (ACPI_FAILURE(status))
+ goto err_workqueue;
+
/*
* On some machines, a software-initiated SMI causes corruption unless
* the SMI runs on CPU 0. An SMI can be initiated by any AML, but
@@ -1161,13 +1178,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* queueing on CPU 0.
*/
ret = queue_work_on(0, queue, &dpc->work);
-
if (!ret) {
printk(KERN_ERR PREFIX
"Call to queue_work() failed.\n");
status = AE_ERROR;
- kfree(dpc);
}
+err_workqueue:
+ if (ACPI_FAILURE(status))
+ kfree(dpc);
+out_thread:
return status;
}
EXPORT_SYMBOL(acpi_os_execute);
@@ -1355,11 +1374,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
chars = strlen(buffer) - 1;
buffer[chars] = '\0';
}
+#else
+ int ret;
+
+ ret = acpi_aml_read_cmd(buffer, buffer_length);
+ if (ret < 0)
+ return AE_ERROR;
+ if (bytes_read)
+ *bytes_read = ret;
#endif

return AE_OK;
}

+acpi_status acpi_os_wait_command_ready(void)
+{
+ int ret;
+
+ ret = acpi_aml_wait_command_ready();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+acpi_status acpi_os_notify_command_complete(void)
+{
+ int ret;
+
+ ret = acpi_aml_notify_command_complete();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
acpi_status acpi_os_signal(u32 function, void *info)
{
switch (function) {
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 69dbae6..e21857d 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -152,8 +152,6 @@
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index 673fdf4..ceea026 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -138,16 +138,6 @@ static inline void acpi_os_terminate_command_signals(void)
{
}

-static inline acpi_status acpi_os_wait_command_ready(void)
-{
- return AE_ERROR;
-}
-
-static inline acpi_status acpi_os_notify_command_complete(void)
-{
- return AE_ERROR;
-}
-
/*
* OSL interfaces added by Linux
*/
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
new file mode 100644
index 0000000..60f3887
--- /dev/null
+++ b/include/linux/acpi_dbg.h
@@ -0,0 +1,52 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_ACPI_DBG_H
+#define _LINUX_ACPI_DBG_H
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_aml_init(void);
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_aml_write_log(const char *msg);
+ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
+int acpi_aml_wait_command_ready(void);
+int acpi_aml_notify_command_complete(void);
+#else
+static int inline acpi_aml_init(void)
+{
+ return 0;
+}
+static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-11-06 06:47:27

by Zheng, Lv

[permalink] [raw]
Subject: [RFC PATCH v2 4/5] tools/power/acpi: Add userspace AML interface support

This patch adds a userspace tool to access Linux kernel AML debugger
interface.

Tow modes are supported by this tool:
1. Interactive: Users are able to launch a debugging shell to talk with
in-kernel AML debugger.
Note that it's user duty to ensure kernel runtime integrity by using
this debugging tool:
A. Some control methods evaluated by the users may result in kernel
panics if those control methods shouldn't be evaluated by the OSPMs
according to the current BIOS/OS configurations.
B. Currently if a single stepping evaluation couldn't run to an end,
then the synchronization primitives acquired by the evaluation may
block normal OSPM control method evaluations.
2. Batch: Users are able to execute debugger commands in a script.
Note that in addition to the above duties, it's user duty to ensure
script runtime integrity by using this debugging tool in this mode:
C. Currently only those commands that are not used for single stepping
are suitable to be used in this mode.
D. If the execution of the command may cause a failure that could result
in an endless kernel execution, the execution of the script may also
get blocked.
To exit the utility, currently "exit/quit" commands are recommended, but
ctrl-C" can also be used.

Signed-off-by: Lv Zheng <[email protected]>
---
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 ++
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++++++++++++++++
3 files changed, 473 insertions(+), 8 deletions(-)
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile
index e882c83..a8bf908 100644
--- a/tools/power/acpi/Makefile
+++ b/tools/power/acpi/Makefile
@@ -10,18 +10,18 @@

include ../../scripts/Makefile.include

-all: acpidump ec
-clean: acpidump_clean ec_clean
-install: acpidump_install ec_install
-uninstall: acpidump_uninstall ec_uninstall
+all: acpidbg acpidump ec
+clean: acpidbg_clean acpidump_clean ec_clean
+install: acpidbg_install acpidump_install ec_install
+uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall

-acpidump ec: FORCE
+acpidbg acpidump ec: FORCE
$(call descend,tools/$@,all)
-acpidump_clean ec_clean:
+acpidbg_clean acpidump_clean ec_clean:
$(call descend,tools/$(@:_clean=),clean)
-acpidump_install ec_install:
+acpidbg_install acpidump_install ec_install:
$(call descend,tools/$(@:_install=),install)
-acpidump_uninstall ec_uninstall:
+acpidbg_uninstall acpidump_uninstall ec_uninstall:
$(call descend,tools/$(@:_uninstall=),uninstall)

.PHONY: FORCE
diff --git a/tools/power/acpi/tools/acpidbg/Makefile b/tools/power/acpi/tools/acpidbg/Makefile
new file mode 100644
index 0000000..352df4b
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/Makefile
@@ -0,0 +1,27 @@
+# tools/power/acpi/tools/acpidbg/Makefile - ACPI tool Makefile
+#
+# Copyright (c) 2015, Intel Corporation
+# Author: Lv Zheng <[email protected]>
+#
+# 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.
+
+include ../../Makefile.config
+
+TOOL = acpidbg
+vpath %.c \
+ ../../../../../drivers/acpi/acpica\
+ ../../common\
+ ../../os_specific/service_layers\
+ .
+CFLAGS += -DACPI_APPLICATION -DACPI_SINGLE_THREAD -DACPI_DEBUGGER\
+ -I.\
+ -I../../../../../drivers/acpi/acpica\
+ -I../../../../../include
+LDFLAGS += -lpthread
+TOOL_OBJS = \
+ acpidbg.o
+
+include ../../Makefile.rules
diff --git a/tools/power/acpi/tools/acpidbg/acpidbg.c b/tools/power/acpi/tools/acpidbg/acpidbg.c
new file mode 100644
index 0000000..e4e8316
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/acpidbg.c
@@ -0,0 +1,438 @@
+/*
+ * ACPI AML interfacing userspace utility
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <acpi/acpi.h>
+
+/* Headers not included by include/acpi/platform/aclinux.h */
+#include <stdbool.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/circ_buf.h>
+
+#define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
+#define ACPI_AML_SEC_TICK 1
+#define ACPI_AML_USEC_PEEK 200
+#define ACPI_AML_BUF_SIZE 4096
+
+#define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
+#define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
+#define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
+
+#define ACPI_AML_LOG_START 0x00
+#define ACPI_AML_PROMPT_START 0x01
+#define ACPI_AML_PROMPT_STOP 0x02
+#define ACPI_AML_LOG_STOP 0x03
+#define ACPI_AML_PROMPT_ROLL 0x04
+
+#define ACPI_AML_INTERACTIVE 0x00
+#define ACPI_AML_BATCH 0x01
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
+#define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
+#define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
+#define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
+
+#define ACPI_AML_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) { \
+ fprintf(stderr, \
+ "%s %s pipe closed.\n", #_buf, #_op); \
+ return; \
+ } \
+ } while (0)
+#define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
+ &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) \
+ return; \
+ } while (0)
+
+
+static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
+static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
+static struct circ_buf acpi_aml_cmd_crc = {
+ .buf = acpi_aml_cmd_buf,
+ .head = 0,
+ .tail = 0,
+};
+static struct circ_buf acpi_aml_log_crc = {
+ .buf = acpi_aml_log_buf,
+ .head = 0,
+ .tail = 0,
+};
+static const char *acpi_aml_file_path = ACPI_AML_FILE;
+static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
+static bool acpi_aml_exit;
+
+static bool acpi_aml_batch_drain;
+static unsigned long acpi_aml_batch_state;
+static char acpi_aml_batch_prompt;
+static char acpi_aml_batch_roll;
+static unsigned long acpi_aml_log_state;
+static char *acpi_aml_batch_cmd = NULL;
+static char *acpi_aml_batch_pos = NULL;
+
+static int acpi_aml_set_fl(int fd, int flags)
+{
+ int ret;
+
+ ret = fcntl(fd, F_GETFL, 0);
+ if (ret < 0) {
+ perror("fcntl(F_GETFL)");
+ return ret;
+ }
+ flags |= ret;
+ ret = fcntl(fd, F_SETFL, flags);
+ if (ret < 0) {
+ perror("fcntl(F_SETFL)");
+ return ret;
+ }
+ return ret;
+}
+
+static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
+{
+ if (fd > maxfd)
+ maxfd = fd;
+ FD_SET(fd, set);
+ return maxfd;
+}
+
+static int acpi_aml_read(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ len = read(fd, p, len);
+ if (len < 0)
+ perror("read");
+ else if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int remained = strlen(acpi_aml_batch_pos);
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ if (len > remained) {
+ memcpy(p, acpi_aml_batch_pos, remained);
+ acpi_aml_batch_pos += remained;
+ len = remained;
+ } else {
+ memcpy(p, acpi_aml_batch_pos, len);
+ acpi_aml_batch_pos += len;
+ }
+ if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int ret = 0;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
+ if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
+ *p = acpi_aml_batch_roll;
+ len = 1;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ } else {
+ len = read(fd, p, 1);
+ if (len <= 0) {
+ if (len < 0)
+ perror("read");
+ ret = len;
+ break;
+ }
+ }
+ switch (acpi_aml_log_state) {
+ case ACPI_AML_LOG_START:
+ if (*p == '\n')
+ acpi_aml_log_state = ACPI_AML_PROMPT_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ break;
+ case ACPI_AML_PROMPT_START:
+ if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
+ *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
+ acpi_aml_batch_prompt = *p;
+ acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
+ } else {
+ if (*p != '\n')
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ case ACPI_AML_PROMPT_STOP:
+ if (*p == ' ') {
+ acpi_aml_log_state = ACPI_AML_LOG_STOP;
+ acpi_aml_exit = true;
+ } else {
+ /* Roll back */
+ acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
+ acpi_aml_batch_roll = *p;
+ *p = acpi_aml_batch_prompt;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int acpi_aml_write(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ else if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ if (!acpi_aml_batch_drain) {
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ }
+ if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
+{
+ int len;
+
+ len = acpi_aml_write(fd, crc);
+ if (circ_count_to_end(crc) == 0)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ return len;
+}
+
+static void acpi_aml_loop(int fd)
+{
+ fd_set rfds;
+ fd_set wfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ if (acpi_aml_mode == ACPI_AML_BATCH) {
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ acpi_aml_batch_pos = acpi_aml_batch_cmd;
+ if (acpi_aml_batch_drain)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ else
+ acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
+ }
+ acpi_aml_exit = false;
+ while (!acpi_aml_exit) {
+ tv.tv_sec = ACPI_AML_SEC_TICK;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (acpi_aml_cmd_space()) {
+ if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
+ maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
+ else if (strlen(acpi_aml_batch_pos) &&
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
+ ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
+ }
+ if (acpi_aml_cmd_count() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
+ if (acpi_aml_log_space() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ if (acpi_aml_log_count())
+ maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
+
+ ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ if (ret < 0) {
+ perror("select");
+ break;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(STDIN_FILENO, &rfds))
+ ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
+ if (FD_ISSET(fd, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, write, cmd, ret);
+ else
+ ACPI_AML_DO(fd, write, cmd, ret);
+ }
+ if (FD_ISSET(fd, &rfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, read, log, ret);
+ else
+ ACPI_AML_DO(fd, read, log, ret);
+ }
+ if (FD_ISSET(STDOUT_FILENO, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
+ else
+ ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
+ }
+ }
+ }
+}
+
+static bool acpi_aml_readable(int fd)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = ACPI_AML_USEC_PEEK;
+ FD_ZERO(&rfds);
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
+ if (ret < 0)
+ perror("select");
+ if (ret > 0 && FD_ISSET(fd, &rfds))
+ return true;
+ return false;
+}
+
+/*
+ * This is a userspace IO flush implementation, replying on the prompt
+ * characters and can be turned into a flush() call after kernel implements
+ * .flush() filesystem operation.
+ */
+static void acpi_aml_flush(int fd)
+{
+ while (acpi_aml_readable(fd)) {
+ acpi_aml_batch_drain = true;
+ acpi_aml_loop(fd);
+ acpi_aml_batch_drain = false;
+ }
+}
+
+void usage(FILE *file, char *progname)
+{
+ fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
+ fprintf(file, "\nOptions:\n");
+ fprintf(file, " -b Specify command to be executed in batch mode\n");
+ fprintf(file, " -f Specify interface file other than");
+ fprintf(file, " /sys/kernel/debug/acpi/aml\n");
+ fprintf(file, " -h Print this help message\n");
+}
+
+int main(int argc, char **argv)
+{
+ int fd = 0;
+ int ch;
+ int len;
+ int ret = EXIT_SUCCESS;
+
+ while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
+ switch (ch) {
+ case 'b':
+ if (acpi_aml_batch_cmd) {
+ fprintf(stderr, "Already specify %s\n",
+ acpi_aml_batch_cmd);
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ len = strlen(optarg);
+ acpi_aml_batch_cmd = calloc(len + 2, 1);
+ if (!acpi_aml_batch_cmd) {
+ perror("calloc");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ memcpy(acpi_aml_batch_cmd, optarg, len);
+ acpi_aml_batch_cmd[len] = '\n';
+ acpi_aml_mode = ACPI_AML_BATCH;
+ break;
+ case 'f':
+ acpi_aml_file_path = optarg;
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ goto exit;
+ break;
+ case '?':
+ default:
+ usage(stderr, argv[0]);
+ ret = EXIT_FAILURE;
+ goto exit;
+ break;
+ }
+ }
+
+ fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ perror("open");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
+ acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
+
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ acpi_aml_flush(fd);
+ acpi_aml_loop(fd);
+
+exit:
+ if (fd < 0)
+ close(fd);
+ if (acpi_aml_batch_cmd)
+ free(acpi_aml_batch_cmd);
+ return ret;
+}
--
1.7.10

2015-11-06 06:47:32

by Zheng, Lv

[permalink] [raw]
Subject: [RFC PATCH v2 5/5] ACPI / debugger: Add module support for ACPI debugger

This patch converts AML debugger into a loadable module.

Note that, it implements driver unloading at the level dependent on the
module reference count. Which means if ACPI debugger is being used by a
userspace program, "rmmod acpi_dbg" should result in failure.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 11 +++
drivers/acpi/Makefile | 2 +-
drivers/acpi/acpi_dbg.c | 80 ++++++++++++------
drivers/acpi/bus.c | 3 +-
drivers/acpi/osl.c | 207 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/acpi.h | 71 ++++++++++++++++
include/linux/acpi_dbg.h | 52 ------------
7 files changed, 338 insertions(+), 88 deletions(-)
delete mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 2b89fd7..c4d4a05 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -66,6 +66,17 @@ config ACPI_DEBUGGER
This is still under development, currently enabling this only
results in the compilation of the ACPICA debugger files.

+if ACPI_DEBUGGER
+
+config ACPI_DEBUGGER_USER
+ tristate "Userspace debugger accessiblity"
+ depends on DEBUG_FS
+ help
+ Export /sys/kernel/debug/acpi/acpidbg for userspace utilities
+ to access the debugger functionalities.
+
+endif
+
config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 102b5e6..c6f236f 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,7 +50,6 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
-acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
@@ -80,6 +79,7 @@ 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
+obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o

# processor has its own "processor." module_param namespace
processor-y := processor_driver.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
index 853ea94..7ca3202 100644
--- a/drivers/acpi/acpi_dbg.c
+++ b/drivers/acpi/acpi_dbg.c
@@ -21,7 +21,7 @@
#include <linux/proc_fs.h>
#include <linux/debugfs.h>
#include <linux/circ_buf.h>
-#include <linux/acpi_dbg.h>
+#include <linux/acpi.h>
#include "internal.h"

#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
@@ -307,7 +307,7 @@ static int acpi_aml_readb_kern(void)
* the debugger output and store the output into the debugger interface
* buffer. Return the size of stored logs or errno.
*/
-ssize_t acpi_aml_write_log(const char *msg)
+static ssize_t acpi_aml_write_log(const char *msg)
{
int ret = 0;
int count = 0, size = 0;
@@ -350,7 +350,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_write_log);

/*
* acpi_aml_read_cmd() - Capture debugger input
@@ -361,7 +360,7 @@ EXPORT_SYMBOL(acpi_aml_write_log);
* the debugger input commands and store the input commands into the
* debugger interface buffer. Return the size of stored commands or errno.
*/
-ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+static ssize_t acpi_aml_read_cmd(char *msg, size_t count)
{
int ret = 0;
int size = 0;
@@ -403,7 +402,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_read_cmd);

static int acpi_aml_thread(void *unsed)
{
@@ -440,7 +438,7 @@ static int acpi_aml_thread(void *unsed)
* This function should be used to implement acpi_os_execute() which is
* used by the ACPICA debugger to create the debugger thread.
*/
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+static int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
{
struct task_struct *t;

@@ -462,30 +460,27 @@ int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
mutex_unlock(&acpi_aml_io.lock);
return 0;
}
-EXPORT_SYMBOL(acpi_aml_create_thread);

-int acpi_aml_wait_command_ready(void)
+static int acpi_aml_wait_command_ready(bool single_step,
+ char *buffer, size_t length)
{
acpi_status status;

- if (!acpi_gbl_method_executing)
- acpi_os_printf("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
- else
+ if (single_step)
acpi_os_printf("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+ else
+ acpi_os_printf("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_get_line(buffer, length, NULL);
if (ACPI_FAILURE(status))
return -EINVAL;
return 0;
}
-EXPORT_SYMBOL(acpi_aml_wait_command_ready);

-int acpi_aml_notify_command_complete(void)
+static int acpi_aml_notify_command_complete(void)
{
return 0;
}
-EXPORT_SYMBOL(acpi_aml_notify_command_complete);

static int acpi_aml_open(struct inode *inode, struct file *file)
{
@@ -759,10 +754,23 @@ static const struct file_operations acpi_aml_operations = {
.llseek = generic_file_llseek,
};

+static const struct acpi_debugger_ops acpi_aml_debugger = {
+ .create_thread = acpi_aml_create_thread,
+ .read_cmd = acpi_aml_read_cmd,
+ .write_log = acpi_aml_write_log,
+ .wait_command_ready = acpi_aml_wait_command_ready,
+ .notify_command_complete = acpi_aml_notify_command_complete,
+};
+
int __init acpi_aml_init(void)
{
- if (!acpi_debugfs_dir)
- return -ENOENT;
+ int ret = 0;
+
+ if (!acpi_debugfs_dir) {
+ ret = -ENOENT;
+ goto err_exit;
+ }
+
/* Initialize AML IO interface */
mutex_init(&acpi_aml_io.lock);
init_waitqueue_head(&acpi_aml_io.wait);
@@ -772,21 +780,39 @@ int __init acpi_aml_init(void)
S_IFREG | S_IRUGO | S_IWUSR,
acpi_debugfs_dir, NULL,
&acpi_aml_operations);
- if (acpi_aml_dentry == NULL)
- return -ENODEV;
+ if (acpi_aml_dentry == NULL) {
+ ret = -ENODEV;
+ goto err_exit;
+ }
+ ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger);
+ if (ret)
+ goto err_fs;
acpi_aml_initialized = true;
- return 0;
+
+err_fs:
+ if (ret) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+err_exit:
+ return ret;
}

-#if 0
void __exit acpi_aml_exit(void)
{
- /* TODO: Stop the in kernel debugger */
- if (acpi_aml_dentry)
- debugfs_remove(acpi_aml_dentry);
- acpi_aml_initialized = false;
+ if (acpi_aml_initialized) {
+ acpi_unregister_debugger(&acpi_aml_debugger);
+ if (acpi_aml_dentry) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+ acpi_aml_initialized = false;
+ }
}

module_init(acpi_aml_init);
module_exit(acpi_aml_exit);
-#endif
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_DESCRIPTION("ACPI debugger userspace IO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 06fbba9..1a40111 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,7 +37,6 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
-#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1095,7 +1094,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
- acpi_aml_init();
+ acpi_debugger_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 2de8f66..ce5ac00 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,7 +40,6 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
-#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -220,6 +219,7 @@ void acpi_os_printf(const char *fmt, ...)
acpi_os_vprintf(fmt, args);
va_end(args);
}
+EXPORT_SYMBOL(acpi_os_printf);

void acpi_os_vprintf(const char *fmt, va_list args)
{
@@ -234,7 +234,7 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- if (acpi_aml_write_log(buffer) < 0)
+ if (acpi_debugger_write_log(buffer) < 0)
printk(KERN_CONT "%s", buffer);
#endif
}
@@ -1100,6 +1100,200 @@ static void acpi_os_execute_deferred(struct work_struct *work)
kfree(dpc);
}

+#ifdef CONFIG_ACPI_DEBUGGER
+static struct acpi_debugger acpi_debugger;
+static bool acpi_debugger_initialized;
+
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_debugger.lock);
+ if (acpi_debugger.ops) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+
+ acpi_debugger.owner = owner;
+ acpi_debugger.ops = ops;
+
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+EXPORT_SYMBOL(acpi_register_debugger);
+
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+ mutex_lock(&acpi_debugger.lock);
+ if (ops == acpi_debugger.ops) {
+ acpi_debugger.ops = NULL;
+ acpi_debugger.owner = NULL;
+ }
+ mutex_unlock(&acpi_debugger.lock);
+}
+EXPORT_SYMBOL(acpi_unregister_debugger);
+
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ int ret;
+ int (*func)(acpi_osd_exec_callback, void *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->create_thread;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(function, context);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_write_log(const char *msg)
+{
+ ssize_t ret;
+ ssize_t (*func)(const char *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->write_log;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(msg);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length)
+{
+ ssize_t ret;
+ ssize_t (*func)(char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->read_cmd;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(buffer, buffer_length);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_wait_command_ready(void)
+{
+ int ret;
+ int (*func)(bool, char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->wait_command_ready;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(acpi_gbl_method_executing,
+ acpi_gbl_db_line_buf, ACPI_DB_LINE_BUFFER_SIZE);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_notify_command_complete(void)
+{
+ int ret;
+ int (*func)(void);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->notify_command_complete;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func();
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int __init acpi_debugger_init(void)
+{
+ mutex_init(&acpi_debugger.lock);
+ acpi_debugger_initialized = true;
+ return 0;
+}
+#endif
+
/*******************************************************************************
*
* FUNCTION: acpi_os_execute
@@ -1127,7 +1321,7 @@ acpi_status acpi_os_execute(acpi_execute_type type,
function, context));

if (type == OSL_DEBUGGER_MAIN_THREAD) {
- ret = acpi_aml_create_thread(function, context);
+ ret = acpi_debugger_create_thread(function, context);
if (ret) {
pr_err("Call to kthread_create() failed.\n");
status = AE_ERROR;
@@ -1377,7 +1571,7 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
#else
int ret;

- ret = acpi_aml_read_cmd(buffer, buffer_length);
+ ret = acpi_debugger_read_cmd(buffer, buffer_length);
if (ret < 0)
return AE_ERROR;
if (bytes_read)
@@ -1386,12 +1580,13 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)

return AE_OK;
}
+EXPORT_SYMBOL(acpi_os_get_line);

acpi_status acpi_os_wait_command_ready(void)
{
int ret;

- ret = acpi_aml_wait_command_ready();
+ ret = acpi_debugger_wait_command_ready();
if (ret < 0)
return AE_ERROR;
return AE_OK;
@@ -1401,7 +1596,7 @@ acpi_status acpi_os_notify_command_complete(void)
{
int ret;

- ret = acpi_aml_notify_command_complete();
+ ret = acpi_debugger_notify_command_complete();
if (ret < 0)
return AE_ERROR;
return AE_OK;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 51a96a8..8f24e03 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -37,6 +37,8 @@
#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/dynamic_debug.h>
+#include <linux/module.h>
+#include <linux/mutex.h>

#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -119,6 +121,75 @@ typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
typedef int (*acpi_tbl_entry_handler)(struct acpi_subtable_header *header,
const unsigned long end);

+/* Debugger support */
+
+struct acpi_debugger_ops {
+ int (*create_thread)(acpi_osd_exec_callback function, void *context);
+ ssize_t (*write_log)(const char *msg);
+ ssize_t (*read_cmd)(char *buffer, size_t length);
+ int (*wait_command_ready)(bool single_step, char *buffer, size_t length);
+ int (*notify_command_complete)(void);
+};
+
+struct acpi_debugger {
+ const struct acpi_debugger_ops *ops;
+ struct module *owner;
+ struct mutex lock;
+};
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_debugger_init(void);
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops);
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops);
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_debugger_write_log(const char *msg);
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length);
+int acpi_debugger_wait_command_ready(void);
+int acpi_debugger_notify_command_complete(void);
+#else
+static inline int acpi_debugger_init(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ return -ENODEV;
+}
+
+static inline void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+}
+
+static inline int acpi_debugger_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size);
#else
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
deleted file mode 100644
index 60f3887..0000000
--- a/include/linux/acpi_dbg.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * ACPI AML interfacing support
- *
- * Copyright (C) 2015, Intel Corporation
- * Authors: Lv Zheng <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _LINUX_ACPI_DBG_H
-#define _LINUX_ACPI_DBG_H
-
-#include <linux/acpi.h>
-
-#ifdef CONFIG_ACPI_DEBUGGER
-int __init acpi_aml_init(void);
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
-ssize_t acpi_aml_write_log(const char *msg);
-ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
-int acpi_aml_wait_command_ready(void);
-int acpi_aml_notify_command_complete(void);
-#else
-static int inline acpi_aml_init(void)
-{
- return 0;
-}
-static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
- void *context)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_write_log(const char *msg)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_wait_command_ready(void)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_notify_command_complete(void)
-{
- return -ENODEV;
-}
-#endif
-
-#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-11-10 08:23:21

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 0/7] ACPICA / debugger: Add in-kernel AML debugger support

This patchset enables ACPICA debugger for Linux kernel and implements a
userspace utility to access it.

A. Build the AML debugger
In order to build the kernel support of AML debugger, the following kconfig
items should be enabled:
CONFIG_ACPI_DEBUG=y
CONFIG_ACPI_DEBUGGER=y
CONFIG_DEBUG_FS=y
CONFIG_ACPI_DEBUGGER_USER=m
The userspace tool can be found at tools/power/acpi/tools/acpidbg. To
build this utility, staying in tools folder and type "make acpi".

B. Load the AML debugger during runtime
In order to use the in-kernel AML debugger, the following command should be
executed using root user:
# modprobe acpi_dbg
# mount -t debugfs none /sys/kernel/debug
# acpidbg

C. Batch mode
In order to support scripts, the userspace utility also supports single
command batch mode:
# acpidbg -b "help"
# acpidbg -b "tables"
# acpidbg -b "find _LID"
# acpidbg -b "execute \_SB.LID0._LID"
You can find the documentation about the ACPICA debugger commands in:
https://acpica.org/sites/acpica/files/acpica-reference_17.pdf
(The latest document can be found at https://acpica.org/documentation)
And refer to the chapter - ACPICA debugger reference to obtain the full
description of the debugger commands. Note that not all commands are
supported by an in-kernel AML debugger.

D. Unload the AML debugger during runtime
After terminating all acpidbg instances, the following command can be
executed to remove the AML debugger from kernel:
# rmmod acpi_dbg

The following tasks are not completed:
1. .flush() support in the kernel debugger IO driver.
2. multi-commands batch mode.

v2:
1. Fix a help message issue for the userspace acpidbg utility (this is a
typo fix).
2. Fix a batch mode issue by adding "\n" to the prompt sequence (this was
lost due to too many rebase cycles).
3. Fix kconfig dependency for an intermediate patch (this is only useful in
case the latter patches are reverted).
4. Add more accurate runtime debugger stub mechanism and remove old
ACPI_DEBUGGER_EXEC() stub mechanism.
5. Add x86 acpi_os_readable() support.

Chen Yu (1):
ACPI / x86: introduce acpi_os_readable() support

Colin Ian King (1):
ACPICA: Debugger: Remove unnecessary status check

Lv Zheng (5):
ACPICA: Debugger: Convert some mechanisms to OSPM specific
ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using
different stub mechanism
ACPI / debugger: Add IO interface to access debugger functionalities
tools/power/acpi: Add userspace AML interface support
ACPI / debugger: Add module support for ACPI debugger

arch/x86/Kconfig | 1 +
arch/x86/include/asm/acenv.h | 1 +
arch/x86/kernel/acpi/boot.c | 14 +
drivers/acpi/Kconfig | 16 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 818 ++++++++++++++++++++++++++++++
drivers/acpi/acpica/acdebug.h | 36 +-
drivers/acpi/acpica/acglobal.h | 5 -
drivers/acpi/acpica/acmacros.h | 11 -
drivers/acpi/acpica/dbdisply.c | 12 +
drivers/acpi/acpica/dbinput.c | 100 +---
drivers/acpi/acpica/dbxface.c | 93 ++--
drivers/acpi/acpica/dscontrol.c | 10 +-
drivers/acpi/acpica/dsutils.c | 16 +-
drivers/acpi/acpica/dswexec.c | 16 +-
drivers/acpi/acpica/utmutex.c | 17 -
drivers/acpi/bus.c | 1 +
drivers/acpi/osl.c | 262 +++++++++-
include/acpi/acpiosxf.h | 18 +-
include/acpi/acpixf.h | 34 ++
include/acpi/platform/aclinux.h | 3 +-
include/acpi/platform/aclinuxex.h | 8 +-
include/linux/acpi.h | 71 +++
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 +
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++
26 files changed, 1833 insertions(+), 212 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

--
1.7.10

2015-11-10 08:23:23

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 1/7] ACPICA: Debugger: Remove unnecessary status check

From: Colin Ian King <[email protected]>

ACPICA commit f9d5c6c9a25e9f5ac05458bfcd8b381e21bb2ba5

ACPICA BZ 1205. Colin Ian King.

Link: https://bugs.acpica.org/show_bug.cgi?id=1205
Link: https://github.com/acpica/acpica/commit/f9d5c6c9
Signed-off-by: Colin Ian King <[email protected]>
Signed-off-by: Bob Moore <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/dbinput.c | 3 ---
1 file changed, 3 deletions(-)

diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index 0480254..fe93f67 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -1246,9 +1246,6 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
* 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,
--
1.7.10

2015-11-10 08:21:56

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 2/7] ACPICA: Debugger: Convert some mechanisms to OSPM specific

The following mechanisms are OSPM specific:
1. Redirect output destination to console: no file redirection will be
needed by an in-kernel debugger, there is even no file can be accessed
when the debugger is running in the kernel mode.
2. Output command prompts: programs other than acpiexec can have different
prompt characters and the prompt characters may be implemented as a
special character sequence to form a char device IO protocol.
3. Command ready/complete handshake: OSPM debugger may wait more conditions
to implement OSPM specific semantics (for example, FIFO full/empty
conditions for O_NONBLOCK or IO open/close conditions).
Leaving such OSPM specific stuffs in the ACPICA debugger core blocks
Linux debugger IO driver implementation.

Several new OSL APIs are provided by this patch:
1. acpi_os_initialize_command_signals: initialize command handshake mechanism
or any other OSPM specific stuffs.
2. acpi_os_terminate_command_signals: reversal of
acpi_os_initialize_command_signals.
3. acpi_os_wait_command_ready: putting debugger task into wait state when a
command is not ready. OSPMs can terminate command loop by returning
AE_CTRL_TERMINATE from this API. Normally, wait_event() or
wait_for_multiple_object() may be used to implement this API.
4. acpi_os_notify_command_complete: putting user task into running state when a
command has been completed. OSPMs can terminate command loop by
returning AE_CTRL_TERMINATE from this API. Normally, wake_up() or
set_event() may be used to implement this API.
This patch also converts current command signaling implementation into a
generic debugger layer (osgendbg.c) to be used by the existing OSPMs or
acpiexec, in return, Linux can have chance to implement its own command
handshake mechanism. This patch also implements acpiexec batch mode in a
multi-threading mode comaptible style as a demo (this can be confirmed by
configuring acpiexec into DEBUGGER_MULTI_THREADED mode where the batch mode
is still working). Lv Zheng.

Note that the OSPM specific command handshake mechanism is required by
Linux kernel because:
1. Linux kernel trends to use wait queue to synchronize two threads, using
mutexes to achieve that will cause false "dead lock" warnings.
2. The command handshake mechanism implemented by ACPICA is implemented in
this way because of a design issue in debugger IO streaming. Debugger IO
outputs are simply cached using a giant buffer, this should be tuned by
Linux in the future.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/acdebug.h | 2 +-
drivers/acpi/acpica/acglobal.h | 5 --
drivers/acpi/acpica/dbinput.c | 97 ++++++++-----------------------------
drivers/acpi/acpica/dbxface.c | 63 +++++++-----------------
drivers/acpi/acpica/utmutex.c | 17 -------
include/acpi/acpiosxf.h | 18 ++++++-
include/acpi/acpixf.h | 11 +++++
include/acpi/platform/aclinux.h | 4 ++
include/acpi/platform/aclinuxex.h | 19 ++++++++
9 files changed, 89 insertions(+), 147 deletions(-)

diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index c928ba4..86474d8 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -257,7 +257,7 @@ acpi_db_command_dispatch(char *input_buffer,

void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context);

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op);
+acpi_status acpi_db_user_commands(void);

char *acpi_db_get_next_token(char *string,
char **next, acpi_object_type * return_type);
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index faa9760..3977134 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -326,7 +326,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list);
#ifdef ACPI_DEBUGGER

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);
@@ -345,7 +344,6 @@ ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]);

/* These buffers should all be the same size */

-ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]);
@@ -360,9 +358,6 @@ 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/dbinput.c b/drivers/acpi/acpica/dbinput.c
index fe93f67..2bf8e6b 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -53,8 +53,6 @@ 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);
@@ -1149,55 +1147,16 @@ acpi_db_command_dispatch(char *input_buffer,

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_db_terminate_loop) {
- 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);
- }
+ (void)acpi_db_user_commands();
acpi_gbl_db_threads_terminated = TRUE;
}

/*******************************************************************************
*
- * 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
+ * PARAMETERS: None
*
* RETURN: None
*
@@ -1206,7 +1165,7 @@ static void acpi_db_single_thread(void)
*
******************************************************************************/

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
+acpi_status acpi_db_user_commands(void)
{
acpi_status status = AE_OK;

@@ -1216,49 +1175,31 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)

while (!acpi_gbl_db_terminate_loop) {

- /* 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 */
+ /* Wait the readiness of the command */

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_wait_command_ready();
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "While parsing command line"));
- return (status);
+ break;
}

- /* Check for single or multithreaded debug */
+ /* Just call to the command line interpreter */

- 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);
+ acpi_gbl_method_executing = FALSE;
+ acpi_gbl_step_to_next_call = FALSE;

- 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 */
+ (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL,
+ NULL);
+
+ /* Notify the completion of the command */

- acpi_db_single_thread();
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ break;
}
}

+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status, "While parsing command line"));
+ }
return (status);
}
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index 342298a..d95e91f 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -85,46 +85,21 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

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);
+ while (status == AE_CTRL_TRUE) {

- /* Different prompt if method is executing */
+ /* Notify the completion of the command */

- if (!acpi_gbl_method_executing) {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_COMMAND_PROMPT);
- } else {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }

- /* Get the user input line */
+ /* Wait the readiness of the command */

- 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_os_wait_command_ready();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
}

status =
@@ -134,6 +109,11 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */

+error_exit:
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While parsing/handling command line"));
+ }
return (status);
}

@@ -420,15 +400,7 @@ acpi_status acpi_initialize_debugger(void)

/* 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);
+ status = acpi_os_initialize_command_signals();
if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get debugger mutex\n");
return_ACPI_STATUS(status);
@@ -473,13 +445,14 @@ void acpi_terminate_debugger(void)
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);
}
+
+ acpi_os_terminate_command_signals();
}

if (acpi_gbl_db_buffer) {
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index ce406e3..ea0c207 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -111,17 +111,6 @@ acpi_status acpi_ut_mutex_initialize(void)
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);
}
@@ -162,12 +151,6 @@ 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/acpiosxf.h b/include/acpi/acpiosxf.h
index fbc2baf..0d824a2 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -349,12 +349,28 @@ void acpi_os_redirect_output(void *destination);
#endif

/*
- * Debug input
+ * Debug IO
*/
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line
acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read);
#endif

+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+acpi_status acpi_os_initialize_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+void acpi_os_terminate_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+acpi_status acpi_os_wait_command_ready(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete
+acpi_status acpi_os_notify_command_complete(void);
+#endif
+
/*
* Obtain ACPI table(s)
*/
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 3aaaa86..95ebae3 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -264,6 +264,15 @@ ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0);

/*
+ * Debugger command handshake globals. Host OSes need to access these
+ * variables to implement their own command handshake mechanism.
+ */
+#ifdef ACPI_DEBUGGER
+ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE);
+ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
+#endif
+
+/*
* Other miscellaneous globals
*/
ACPI_GLOBAL(struct acpi_table_fadt, acpi_gbl_FADT);
@@ -929,6 +938,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
void **data,
void (*callback)(void *)))

+void acpi_run_debugger(char *batch_buffer);
+
void acpi_set_debugger_thread_id(acpi_thread_id thread_id);

#endif /* __ACXFACE_H__ */
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 323e5da..69dbae6 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -150,6 +150,10 @@
*/
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index fd6d70f..673fdf4 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -129,6 +129,25 @@ static inline u8 acpi_os_readable(void *pointer, acpi_size length)
return TRUE;
}

+static inline acpi_status acpi_os_initialize_command_signals(void)
+{
+ return AE_OK;
+}
+
+static inline void acpi_os_terminate_command_signals(void)
+{
+}
+
+static inline acpi_status acpi_os_wait_command_ready(void)
+{
+ return AE_ERROR;
+}
+
+static inline acpi_status acpi_os_notify_command_complete(void)
+{
+ return AE_ERROR;
+}
+
/*
* OSL interfaces added by Linux
*/
--
1.7.10

2015-11-10 08:22:03

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 3/7] ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using different stub mechanism

The ACPI_DEBUGGER_EXEC is a problem now when the debugger code is compiled
but runtime disabled. They actually will get executed in this situation.
Although such executions are harmless if we can correctly make
acpi_db_single_step() a runtime stub, users may still do not want to see the
debugger print messages logged into OSPMs' kernel logs when a debugger
driver is not loaded to enable the debugger during runtime.

This patch fixes this issue by introducing new stub mechanism instead of
ACPI_DEBUGGER_EXEC. Lv Zheng.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/acdebug.h | 34 ++++++++++++++++++++++++----------
drivers/acpi/acpica/acmacros.h | 11 -----------
drivers/acpi/acpica/dbdisply.c | 12 ++++++++++++
drivers/acpi/acpica/dbxface.c | 30 ++++++++++++++++++++++++++++++
drivers/acpi/acpica/dscontrol.c | 10 ++--------
drivers/acpi/acpica/dsutils.c | 16 ++++++++--------
drivers/acpi/acpica/dswexec.c | 16 +++++++---------
include/acpi/acpixf.h | 23 +++++++++++++++++++++++
8 files changed, 106 insertions(+), 46 deletions(-)

diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index 86474d8..dcaa15d 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -80,9 +80,15 @@ struct acpi_db_execute_walk {
/*
* dbxface - external debugger interfaces
*/
-acpi_status
-acpi_db_single_step(struct acpi_walk_state *walk_state,
- union acpi_parse_object *op, u32 op_type);
+ACPI_DBR_DEPENDENT_RETURN_OK(acpi_status
+ acpi_db_single_step(struct acpi_walk_state
+ *walk_state,
+ union acpi_parse_object *op,
+ u32 op_type))
+ ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_signal_break_point(struct
+ acpi_walk_state
+ *walk_state))

/*
* dbcmds - debug commands and output routines
@@ -182,11 +188,15 @@ void acpi_db_display_method_info(union acpi_parse_object *op);

void acpi_db_decode_and_display_object(char *target, char *output_type);

-void
-acpi_db_display_result_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_result_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))

-acpi_status acpi_db_display_all_methods(char *display_count_arg);
+ acpi_status acpi_db_display_all_methods(char *display_count_arg);

void acpi_db_display_arguments(void);

@@ -198,9 +208,13 @@ void acpi_db_display_calling_tree(void);

void acpi_db_display_object_type(char *object_arg);

-void
-acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_argument_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))

/*
* dbexec - debugger control method execution
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index e85366c..bad5bca 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -401,17 +401,6 @@
#endif

/*
- * Some code only gets executed when the debugger is built in.
- * Note that this is entirely independent of whether the
- * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not.
- */
-#ifdef ACPI_DEBUGGER
-#define ACPI_DEBUGGER_EXEC(a) a
-#else
-#define ACPI_DEBUGGER_EXEC(a)
-#endif
-
-/*
* Macros used for ACPICA utilities only
*/

diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c
index 672977e..c42ce8a 100644
--- a/drivers/acpi/acpica/dbdisply.c
+++ b/drivers/acpi/acpica/dbdisply.c
@@ -679,6 +679,12 @@ acpi_db_display_result_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{

+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
/* Only display if single stepping */

if (!acpi_gbl_cm_single_step) {
@@ -708,6 +714,12 @@ acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{

+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
if (!acpi_gbl_cm_single_step) {
return;
}
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index d95e91f..d7ff58e 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -119,6 +119,36 @@ error_exit:

/*******************************************************************************
*
+ * FUNCTION: acpi_db_signal_break_point
+ *
+ * PARAMETERS: walk_state - Current walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called for AML_BREAK_POINT_OP
+ *
+ ******************************************************************************/
+
+void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)
+{
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
+ /*
+ * Set the single-step flag. This will cause the debugger (if present)
+ * to break to the console within the AML debugger at the start of the
+ * next AML instruction.
+ */
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_os_printf("**break** Executed AML BreakPoint opcode\n");
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_db_single_step
*
* PARAMETERS: walk_state - Current walk
diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c
index 435fc16..06a6f7f 100644
--- a/drivers/acpi/acpica/dscontrol.c
+++ b/drivers/acpi/acpica/dscontrol.c
@@ -47,6 +47,7 @@
#include "amlcode.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "acdebug.h"

#define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME("dscontrol")
@@ -348,14 +349,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state,

case AML_BREAK_POINT_OP:

- /*
- * Set the single-step flag. This will cause the debugger (if present)
- * to break to the console within the AML debugger at the start of the
- * next AML instruction.
- */
- ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE);
- ACPI_DEBUGGER_EXEC(acpi_os_printf
- ("**break** Executed AML BreakPoint opcode\n"));
+ acpi_db_signal_break_point(walk_state);

/* Call to the OSL in case OS wants a piece of the action */

diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index ebc577b..e4293a8 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -605,8 +605,8 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+
+ acpi_db_display_argument_object(obj_desc, walk_state);
} else {
/* Check for null name case */

@@ -638,10 +638,11 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"Argument previously created, already stacked\n"));

- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (walk_state->
- operands[walk_state->num_operands -
- 1], walk_state));
+ acpi_db_display_argument_object(walk_state->
+ operands[walk_state->
+ num_operands -
+ 1],
+ walk_state);

/*
* Use value that was already previously returned
@@ -685,8 +686,7 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(status);
}

- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+ acpi_db_display_argument_object(obj_desc, walk_state);
}

return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index df54d46..9cc5761 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -178,8 +178,7 @@ cleanup:

/* Break to debugger to display result */

- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (local_obj_desc, walk_state));
+ acpi_db_display_result_object(local_obj_desc, walk_state);

/*
* Delete the predicate result object (we know that
@@ -386,11 +385,10 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)

/* Call debugger for single step support (DEBUG build only) */

- ACPI_DEBUGGER_EXEC(status =
- acpi_db_single_step(walk_state, op, op_class));
- ACPI_DEBUGGER_EXEC(if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);}
- ) ;
+ status = acpi_db_single_step(walk_state, op, op_class);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }

/* Decode the Opcode Class */

@@ -728,8 +726,8 @@ cleanup:

/* Break to debugger to display result */

- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (walk_state->result_obj, walk_state));
+ acpi_db_display_result_object(walk_state->result_obj,
+ walk_state);

/*
* Delete the result op if and only if:
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 95ebae3..5dfab9c 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -375,6 +375,29 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);

#endif /* ACPI_APPLICATION */

+/*
+ * Debugger prototypes
+ *
+ * All interfaces used by debugger will be configured
+ * out of the ACPICA build unless the ACPI_DEBUGGER
+ * flag is defined.
+ */
+#ifdef ACPI_DEBUGGER
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ ACPI_EXTERNAL_RETURN_OK(prototype)
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ ACPI_EXTERNAL_RETURN_VOID(prototype)
+
+#else
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ static ACPI_INLINE prototype {return(AE_OK);}
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ static ACPI_INLINE prototype {return;}
+
+#endif /* ACPI_DEBUGGER */
+
/*****************************************************************************
*
* ACPICA public interface prototypes
--
1.7.10

2015-11-10 08:22:11

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 4/7] ACPI / debugger: Add IO interface to access debugger functionalities

This patch adds /sys/kernel/debug/acpi/acpidbg, which can be used by
userspace programs to access ACPICA debugger functionalities.

Known issue:
1. IO flush support
acpi_os_notify_command_complete() and acpi_os_wait_command_ready() can
be used by acpi_dbg module to implement .flush() filesystem operation.
While this patch doesn't go that far. It then becomes userspace tool's
duty now to flush old commands before executing new batch mode commands.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 3 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 792 +++++++++++++++++++++++++++++++++++++
drivers/acpi/bus.c | 2 +
drivers/acpi/osl.c | 55 ++-
include/acpi/platform/aclinux.h | 2 -
include/acpi/platform/aclinuxex.h | 10 -
include/linux/acpi_dbg.h | 52 +++
8 files changed, 900 insertions(+), 17 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 25dbb76..4de3517 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -58,8 +58,9 @@ config ACPI_CCA_REQUIRED
bool

config ACPI_DEBUGGER
- bool "In-kernel debugger (EXPERIMENTAL)"
+ bool "In-kernel debugger"
select ACPI_DEBUG
+ depends on DEBUG_FS
help
Enable in-kernel debugging facilities: statistics, internal
object dump, single step control method execution.
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..102b5e6 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,6 +50,7 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
+acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
new file mode 100644
index 0000000..7be0851
--- /dev/null
+++ b/drivers/acpi/acpi_dbg.c
@@ -0,0 +1,792 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI : AML: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/acpi_dbg.h>
+#include "internal.h"
+
+#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
+#define ACPI_AML_BUF_SIZE PAGE_SIZE
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define ACPI_AML_OPENED 0x0001
+#define ACPI_AML_CLOSED 0x0002
+#define ACPI_AML_IN_USER 0x0004 /* user space is writing cmd */
+#define ACPI_AML_IN_KERN 0x0008 /* kernel space is reading cmd */
+#define ACPI_AML_OUT_USER 0x0010 /* user space is reading log */
+#define ACPI_AML_OUT_KERN 0x0020 /* kernel space is writing log */
+#define ACPI_AML_USER (ACPI_AML_IN_USER | ACPI_AML_OUT_USER)
+#define ACPI_AML_KERN (ACPI_AML_IN_KERN | ACPI_AML_OUT_KERN)
+#define ACPI_AML_BUSY (ACPI_AML_USER | ACPI_AML_KERN)
+#define ACPI_AML_OPEN (ACPI_AML_OPENED | ACPI_AML_CLOSED)
+
+struct acpi_aml_io {
+ wait_queue_head_t wait;
+ unsigned long flags;
+ unsigned long users;
+ struct mutex lock;
+ struct task_struct *thread;
+ char out_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf out_crc;
+ char in_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf in_crc;
+ acpi_osd_exec_callback function;
+ void *context;
+ unsigned long usages;
+};
+
+static struct acpi_aml_io acpi_aml_io;
+static bool acpi_aml_initialized;
+static struct file *acpi_aml_active_reader;
+static struct dentry *acpi_aml_dentry;
+
+static inline bool __acpi_aml_running(void)
+{
+ return acpi_aml_io.thread ? true : false;
+}
+
+static inline bool __acpi_aml_access_ok(unsigned long flag)
+{
+ /*
+ * The debugger interface is in opened state (OPENED && !CLOSED),
+ * then it is allowed to access the debugger buffers from either
+ * user space or the kernel space.
+ * In addition, for the kernel space, only the debugger thread
+ * (thread ID matched) is allowed to access.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED) ||
+ (acpi_aml_io.flags & ACPI_AML_CLOSED) ||
+ !__acpi_aml_running())
+ return false;
+ if ((flag & ACPI_AML_KERN) &&
+ current != acpi_aml_io.thread)
+ return false;
+ return true;
+}
+
+static inline bool __acpi_aml_readable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another read is not in progress and there is data in buffer
+ * available for read.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_count(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_writable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another write is not in progress and there is buffer space
+ * available for write.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_space(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_busy(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_BUSY)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_opened(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_OPEN)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_used(void)
+{
+ return acpi_aml_io.usages ? true : false;
+}
+
+static inline bool acpi_aml_running(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_running();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_busy(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_busy();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_used(void)
+{
+ bool ret;
+
+ /*
+ * The usage count is prepared to avoid race conditions between the
+ * starts and the stops of the debugger thread.
+ */
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_used();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_KERN) ||
+ __acpi_aml_readable(&acpi_aml_io.in_crc, ACPI_AML_IN_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_KERN) ||
+ __acpi_aml_writable(&acpi_aml_io.out_crc, ACPI_AML_OUT_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_USER) ||
+ __acpi_aml_readable(&acpi_aml_io.out_crc, ACPI_AML_OUT_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_USER) ||
+ __acpi_aml_writable(&acpi_aml_io.in_crc, ACPI_AML_IN_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_write(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_writable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_read(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_readable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static void acpi_aml_unlock_fifo(unsigned long flag, bool wakeup)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~flag;
+ if (wakeup)
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+}
+
+static int acpi_aml_write_kern(const char *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting logs */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ memcpy(p, buf, n);
+ /* sync head after inserting logs */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_KERN, true);
+ return n;
+}
+
+static int acpi_aml_readb_kern(void)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing cmds */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ ret = (int)*p;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ crc->tail = (crc->tail + 1) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_IN_KERN, true);
+ return ret;
+}
+
+/*
+ * acpi_aml_write_log() - Capture debugger output
+ * @msg: the debugger output
+ *
+ * This function should be used to implement acpi_os_printf() to filter out
+ * the debugger output and store the output into the debugger interface
+ * buffer. Return the size of stored logs or errno.
+ */
+ssize_t acpi_aml_write_log(const char *msg)
+{
+ int ret = 0;
+ int count = 0, size = 0;
+
+ if (!acpi_aml_initialized)
+ return -ENODEV;
+ if (msg)
+ count = strlen(msg);
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_kern(msg + size, count);
+ /*
+ * A control method execution may be blocked if we wait
+ * user space here and some kernel functionalities may be
+ * executing another control method that need to be
+ * synchronized with the one executed by the debugger
+ * thread. Thus we do not wait when the user space isn't
+ * fast enough to drain the logs, so that the logs are
+ * dropped when the buffer is overflow and the normal
+ * kernel functionalities won't get blocked.
+ *
+ * A module parameter may be used to tune this if the
+ * integrity of the debugger output is required.
+ */
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ break;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ size += ret;
+ count -= ret;
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_write_log);
+
+/*
+ * acpi_aml_read_cmd() - Capture debugger input
+ * @msg: the debugger input
+ * @size: the size of the debugger input
+ *
+ * This function should be used to implement acpi_os_get_line() to capture
+ * the debugger input commands and store the input commands into the
+ * debugger interface buffer. Return the size of stored commands or errno.
+ */
+ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+{
+ int ret = 0;
+ int size = 0;
+
+ /*
+ * This is ensured by the running fact of the debugger thread
+ * unless a bug is introduced.
+ */
+ BUG_ON(!acpi_aml_initialized);
+ while (count > 0) {
+again:
+ /*
+ * Check each input byte to find the end of the command.
+ */
+ ret = acpi_aml_readb_kern();
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_readable());
+ /*
+ * We need to retry when the condition becomes
+ * true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ *(msg + size) = (char)ret;
+ size++;
+ count--;
+ if (ret == '\n') {
+ /*
+ * acpi_os_get_line() requires a zero terminated command
+ * string.
+ */
+ *(msg + size - 1) = '\0';
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_read_cmd);
+
+static int acpi_aml_thread(void *unsed)
+{
+ acpi_osd_exec_callback function = NULL;
+ void *context;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (acpi_aml_io.function) {
+ acpi_aml_io.usages++;
+ function = acpi_aml_io.function;
+ context = acpi_aml_io.context;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ if (function)
+ function(context);
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.usages--;
+ if (!__acpi_aml_used()) {
+ acpi_aml_io.thread = NULL;
+ wake_up(&acpi_aml_io.wait);
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ return 0;
+}
+
+/*
+ * acpi_aml_create_thread() - Create AML debugger thread
+ * @function: the debugger thread callback
+ * @context: the context to be passed to the debugger thread
+ *
+ * This function should be used to implement acpi_os_execute() which is
+ * used by the ACPICA debugger to create the debugger thread.
+ */
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ struct task_struct *t;
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.function = function;
+ acpi_aml_io.context = context;
+ mutex_unlock(&acpi_aml_io.lock);
+
+ t = kthread_create(acpi_aml_thread, NULL, "aml");
+ if (IS_ERR(t)) {
+ pr_err("Failed to create AML debugger thread.\n");
+ return PTR_ERR(t);
+ }
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.thread = t;
+ acpi_set_debugger_thread_id((acpi_thread_id)(unsigned long)t);
+ wake_up_process(t);
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_create_thread);
+
+int acpi_aml_wait_command_ready(void)
+{
+ acpi_status status;
+
+ if (!acpi_gbl_method_executing)
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+
+ status = acpi_os_get_line(acpi_gbl_db_line_buf,
+ ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_wait_command_ready);
+
+int acpi_aml_notify_command_complete(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_notify_command_complete);
+
+static int acpi_aml_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ acpi_status status;
+
+ mutex_lock(&acpi_aml_io.lock);
+ /*
+ * The debugger interface is being closed, no new user is allowed
+ * during this period.
+ */
+ if (acpi_aml_io.flags & ACPI_AML_CLOSED) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ /*
+ * Only one reader is allowed to initiate the debugger
+ * thread.
+ */
+ if (acpi_aml_active_reader) {
+ ret = -EBUSY;
+ goto err_lock;
+ } else {
+ pr_debug("Opening debugger reader.\n");
+ acpi_aml_active_reader = file;
+ }
+ } else {
+ /*
+ * No writer is allowed unless the debugger thread is
+ * ready.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ }
+ if (acpi_aml_active_reader == file) {
+ pr_debug("Opening debugger interface.\n");
+ mutex_unlock(&acpi_aml_io.lock);
+
+ pr_debug("Initializing debugger thread.\n");
+ status = acpi_initialize_debugger();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Failed to initialize debugger.\n");
+ ret = -EINVAL;
+ goto err_lock;
+ }
+ acpi_aml_io.flags |= ACPI_AML_OPENED;
+ pr_debug("Debugger thread initialized.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0;
+ acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0;
+ pr_debug("Debugger interface opened.\n");
+ }
+ acpi_aml_io.users++;
+err_lock:
+ if (IS_ERR_VALUE(ret)) {
+ if (acpi_aml_active_reader == file)
+ acpi_aml_active_reader = NULL;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.users--;
+ if (file == acpi_aml_active_reader) {
+ pr_debug("Closing debugger reader.\n");
+ acpi_aml_active_reader = NULL;
+
+ pr_debug("Closing debugger interface.\n");
+ acpi_aml_io.flags |= ACPI_AML_CLOSED;
+
+ /*
+ * Wake up all user space/kernel space blocked
+ * readers/writers.
+ */
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+ /*
+ * Wait all user space/kernel space readers/writers to
+ * stop so that ACPICA command loop of the debugger thread
+ * should fail all its command line reads after this point.
+ */
+ wait_event(acpi_aml_io.wait, !acpi_aml_busy());
+
+ /*
+ * Then we try to terminate the debugger thread if it is
+ * not terminated.
+ */
+ pr_debug("Terminating debugger thread.\n");
+ acpi_terminate_debugger();
+ wait_event(acpi_aml_io.wait, !acpi_aml_used());
+ pr_debug("Debugger thread terminated.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~ACPI_AML_OPENED;
+ }
+ if (acpi_aml_io.users == 0) {
+ pr_debug("Debugger interface closed.\n");
+ acpi_aml_io.flags &= ~ACPI_AML_CLOSED;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_read_user(char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing logs */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ n = min(len, circ_count_to_end(crc));
+ ret = copy_to_user(buf, p, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync tail after removing logs */
+ smp_mb();
+ crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !IS_ERR_VALUE(ret));
+ return ret;
+}
+
+static ssize_t acpi_aml_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_read_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_readable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_write_user(const char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ ret = copy_from_user(p, buf, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync head after inserting cmds */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !IS_ERR_VALUE(ret));
+ return n;
+}
+
+static ssize_t acpi_aml_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static unsigned int acpi_aml_poll(struct file *file, poll_table *wait)
+{
+ int masks = 0;
+
+ poll_wait(file, &acpi_aml_io.wait, wait);
+ if (acpi_aml_user_readable())
+ masks |= POLLIN | POLLRDNORM;
+ if (acpi_aml_user_writable())
+ masks |= POLLOUT | POLLWRNORM;
+
+ return masks;
+}
+
+static const struct file_operations acpi_aml_operations = {
+ .read = acpi_aml_read,
+ .write = acpi_aml_write,
+ .poll = acpi_aml_poll,
+ .open = acpi_aml_open,
+ .release = acpi_aml_release,
+ .llseek = generic_file_llseek,
+};
+
+int __init acpi_aml_init(void)
+{
+ if (!acpi_debugfs_dir)
+ return -ENOENT;
+ /* Initialize AML IO interface */
+ mutex_init(&acpi_aml_io.lock);
+ init_waitqueue_head(&acpi_aml_io.wait);
+ acpi_aml_io.out_crc.buf = acpi_aml_io.out_buf;
+ acpi_aml_io.in_crc.buf = acpi_aml_io.in_buf;
+ acpi_aml_dentry = debugfs_create_file("acpidbg",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ acpi_debugfs_dir, NULL,
+ &acpi_aml_operations);
+ if (acpi_aml_dentry == NULL)
+ return -ENODEV;
+ acpi_aml_initialized = true;
+ return 0;
+}
+
+#if 0
+void __exit acpi_aml_exit(void)
+{
+ /* TODO: Stop the in kernel debugger */
+ if (acpi_aml_dentry)
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_initialized = false;
+}
+
+module_init(acpi_aml_init);
+module_exit(acpi_aml_exit);
+#endif
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a212cef..06fbba9 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,6 +37,7 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
+#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1094,6 +1095,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
+ acpi_aml_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 3272915..2de8f66 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,6 +40,7 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
+#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -233,7 +234,8 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- printk(KERN_CONT "%s", buffer);
+ if (acpi_aml_write_log(buffer) < 0)
+ printk(KERN_CONT "%s", buffer);
#endif
}

@@ -1124,6 +1126,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));

+ if (type == OSL_DEBUGGER_MAIN_THREAD) {
+ ret = acpi_aml_create_thread(function, context);
+ if (ret) {
+ pr_err("Call to kthread_create() failed.\n");
+ status = AE_ERROR;
+ }
+ goto out_thread;
+ }
+
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the work_struct list in a
@@ -1148,11 +1159,17 @@ acpi_status acpi_os_execute(acpi_execute_type type,
if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else {
+ } else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ } else {
+ pr_err("Unsupported os_execute type %d.\n", type);
+ status = AE_ERROR;
}

+ if (ACPI_FAILURE(status))
+ goto err_workqueue;
+
/*
* On some machines, a software-initiated SMI causes corruption unless
* the SMI runs on CPU 0. An SMI can be initiated by any AML, but
@@ -1161,13 +1178,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* queueing on CPU 0.
*/
ret = queue_work_on(0, queue, &dpc->work);
-
if (!ret) {
printk(KERN_ERR PREFIX
"Call to queue_work() failed.\n");
status = AE_ERROR;
- kfree(dpc);
}
+err_workqueue:
+ if (ACPI_FAILURE(status))
+ kfree(dpc);
+out_thread:
return status;
}
EXPORT_SYMBOL(acpi_os_execute);
@@ -1355,11 +1374,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
chars = strlen(buffer) - 1;
buffer[chars] = '\0';
}
+#else
+ int ret;
+
+ ret = acpi_aml_read_cmd(buffer, buffer_length);
+ if (ret < 0)
+ return AE_ERROR;
+ if (bytes_read)
+ *bytes_read = ret;
#endif

return AE_OK;
}

+acpi_status acpi_os_wait_command_ready(void)
+{
+ int ret;
+
+ ret = acpi_aml_wait_command_ready();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+acpi_status acpi_os_notify_command_complete(void)
+{
+ int ret;
+
+ ret = acpi_aml_notify_command_complete();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
acpi_status acpi_os_signal(u32 function, void *info)
{
switch (function) {
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 69dbae6..e21857d 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -152,8 +152,6 @@
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index 673fdf4..ceea026 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -138,16 +138,6 @@ static inline void acpi_os_terminate_command_signals(void)
{
}

-static inline acpi_status acpi_os_wait_command_ready(void)
-{
- return AE_ERROR;
-}
-
-static inline acpi_status acpi_os_notify_command_complete(void)
-{
- return AE_ERROR;
-}
-
/*
* OSL interfaces added by Linux
*/
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
new file mode 100644
index 0000000..60f3887
--- /dev/null
+++ b/include/linux/acpi_dbg.h
@@ -0,0 +1,52 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_ACPI_DBG_H
+#define _LINUX_ACPI_DBG_H
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_aml_init(void);
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_aml_write_log(const char *msg);
+ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
+int acpi_aml_wait_command_ready(void);
+int acpi_aml_notify_command_complete(void);
+#else
+static int inline acpi_aml_init(void)
+{
+ return 0;
+}
+static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-11-10 08:22:19

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support

From: Chen Yu <[email protected]>

This patch implements acpi_os_readable(). The function is used by ACPI
AML debugger to validate user specified pointers for the dumpable AML
operand objects.

Signed-off-by: Chen Yu <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/acenv.h | 1 +
arch/x86/kernel/acpi/boot.c | 14 ++++++++++++++
drivers/acpi/Kconfig | 3 +++
drivers/acpi/osl.c | 12 ++++++++++++
include/acpi/platform/aclinux.h | 1 -
include/acpi/platform/aclinuxex.h | 5 -----
7 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 96d058a..25585ee 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -19,6 +19,7 @@ config X86
def_bool y
select ACPI_LEGACY_TABLES_LOOKUP if ACPI
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
+ select ACPI_MEMORY_ACCESS_CHECK_SUPPORT if ACPI
select ANON_INODES
select ARCH_CLOCKSOURCE_DATA
select ARCH_DISCARD_MEMBLOCK
diff --git a/arch/x86/include/asm/acenv.h b/arch/x86/include/asm/acenv.h
index 1b010a8..a2a2849 100644
--- a/arch/x86/include/asm/acenv.h
+++ b/arch/x86/include/asm/acenv.h
@@ -20,6 +20,7 @@

int __acpi_acquire_global_lock(unsigned int *lock);
int __acpi_release_global_lock(unsigned int *lock);
+bool __acpi_memory_readable(void *pointer, size_t length);

#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \
((Acq) = __acpi_acquire_global_lock(&facs->global_lock))
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index e759076..3e03fde 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -1699,6 +1699,20 @@ int __acpi_release_global_lock(unsigned int *lock)
return old & 0x1;
}

+bool __acpi_memory_readable(void *pointer, size_t length)
+{
+ unsigned long start_pfn, end_pfn;
+
+ start_pfn = page_to_pfn(virt_to_page(pointer));
+ end_pfn = page_to_pfn(virt_to_page(pointer + length));
+ /*
+ * Since the acpi address is allocated by kmalloc, we only
+ * need to consider the direct-mapping virtual address,
+ * rather than the kmap/vmalloc/ioremap address.
+ */
+ return pfn_range_is_mapped(start_pfn, end_pfn) ? true : false;
+}
+
void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
{
e820_add_region(addr, size, E820_ACPI);
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 4de3517..e67b1b5 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT
config ACPI_CCA_REQUIRED
bool

+config ACPI_MEMORY_ACCESS_CHECK_SUPPORT
+ bool
+
config ACPI_DEBUGGER
bool "In-kernel debugger"
select ACPI_DEBUG
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 2de8f66..bc3da95 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -1387,6 +1387,18 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
return AE_OK;
}

+#ifdef CONFIG_ACPI_MEMORY_ACCESS_CHECK_SUPPORT
+u8 acpi_os_readable(void *pointer, acpi_size length)
+{
+ return __acpi_memory_readable(pointer, length);
+}
+#else
+u8 acpi_os_readable(void *pointer, acpi_size length)
+{
+ return true;
+}
+#endif
+
acpi_status acpi_os_wait_command_ready(void)
{
int ret;
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index e21857d..b3c493e 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -148,7 +148,6 @@
/*
* OSL interfaces used by debugger/disassembler
*/
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index ceea026..94bead6 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -124,11 +124,6 @@ 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;
-}
-
static inline acpi_status acpi_os_initialize_command_signals(void)
{
return AE_OK;
--
1.7.10

2015-11-10 08:22:25

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 6/7] tools/power/acpi: Add userspace AML interface support

This patch adds a userspace tool to access Linux kernel AML debugger
interface.

Tow modes are supported by this tool:
1. Interactive: Users are able to launch a debugging shell to talk with
in-kernel AML debugger.
Note that it's user duty to ensure kernel runtime integrity by using
this debugging tool:
A. Some control methods evaluated by the users may result in kernel
panics if those control methods shouldn't be evaluated by the OSPMs
according to the current BIOS/OS configurations.
B. Currently if a single stepping evaluation couldn't run to an end,
then the synchronization primitives acquired by the evaluation may
block normal OSPM control method evaluations.
2. Batch: Users are able to execute debugger commands in a script.
Note that in addition to the above duties, it's user duty to ensure
script runtime integrity by using this debugging tool in this mode:
C. Currently only those commands that are not used for single stepping
are suitable to be used in this mode.
D. If the execution of the command may cause a failure that could result
in an endless kernel execution, the execution of the script may also
get blocked.
To exit the utility, currently "exit/quit" commands are recommended, but
ctrl-C" can also be used.

Signed-off-by: Lv Zheng <[email protected]>
---
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 ++
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++++++++++++++++
3 files changed, 473 insertions(+), 8 deletions(-)
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile
index e882c83..a8bf908 100644
--- a/tools/power/acpi/Makefile
+++ b/tools/power/acpi/Makefile
@@ -10,18 +10,18 @@

include ../../scripts/Makefile.include

-all: acpidump ec
-clean: acpidump_clean ec_clean
-install: acpidump_install ec_install
-uninstall: acpidump_uninstall ec_uninstall
+all: acpidbg acpidump ec
+clean: acpidbg_clean acpidump_clean ec_clean
+install: acpidbg_install acpidump_install ec_install
+uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall

-acpidump ec: FORCE
+acpidbg acpidump ec: FORCE
$(call descend,tools/$@,all)
-acpidump_clean ec_clean:
+acpidbg_clean acpidump_clean ec_clean:
$(call descend,tools/$(@:_clean=),clean)
-acpidump_install ec_install:
+acpidbg_install acpidump_install ec_install:
$(call descend,tools/$(@:_install=),install)
-acpidump_uninstall ec_uninstall:
+acpidbg_uninstall acpidump_uninstall ec_uninstall:
$(call descend,tools/$(@:_uninstall=),uninstall)

.PHONY: FORCE
diff --git a/tools/power/acpi/tools/acpidbg/Makefile b/tools/power/acpi/tools/acpidbg/Makefile
new file mode 100644
index 0000000..352df4b
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/Makefile
@@ -0,0 +1,27 @@
+# tools/power/acpi/tools/acpidbg/Makefile - ACPI tool Makefile
+#
+# Copyright (c) 2015, Intel Corporation
+# Author: Lv Zheng <[email protected]>
+#
+# 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.
+
+include ../../Makefile.config
+
+TOOL = acpidbg
+vpath %.c \
+ ../../../../../drivers/acpi/acpica\
+ ../../common\
+ ../../os_specific/service_layers\
+ .
+CFLAGS += -DACPI_APPLICATION -DACPI_SINGLE_THREAD -DACPI_DEBUGGER\
+ -I.\
+ -I../../../../../drivers/acpi/acpica\
+ -I../../../../../include
+LDFLAGS += -lpthread
+TOOL_OBJS = \
+ acpidbg.o
+
+include ../../Makefile.rules
diff --git a/tools/power/acpi/tools/acpidbg/acpidbg.c b/tools/power/acpi/tools/acpidbg/acpidbg.c
new file mode 100644
index 0000000..d070fcc
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/acpidbg.c
@@ -0,0 +1,438 @@
+/*
+ * ACPI AML interfacing userspace utility
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <acpi/acpi.h>
+
+/* Headers not included by include/acpi/platform/aclinux.h */
+#include <stdbool.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/circ_buf.h>
+
+#define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
+#define ACPI_AML_SEC_TICK 1
+#define ACPI_AML_USEC_PEEK 200
+#define ACPI_AML_BUF_SIZE 4096
+
+#define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
+#define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
+#define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
+
+#define ACPI_AML_LOG_START 0x00
+#define ACPI_AML_PROMPT_START 0x01
+#define ACPI_AML_PROMPT_STOP 0x02
+#define ACPI_AML_LOG_STOP 0x03
+#define ACPI_AML_PROMPT_ROLL 0x04
+
+#define ACPI_AML_INTERACTIVE 0x00
+#define ACPI_AML_BATCH 0x01
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
+#define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
+#define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
+#define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
+
+#define ACPI_AML_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) { \
+ fprintf(stderr, \
+ "%s %s pipe closed.\n", #_buf, #_op); \
+ return; \
+ } \
+ } while (0)
+#define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
+ &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) \
+ return; \
+ } while (0)
+
+
+static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
+static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
+static struct circ_buf acpi_aml_cmd_crc = {
+ .buf = acpi_aml_cmd_buf,
+ .head = 0,
+ .tail = 0,
+};
+static struct circ_buf acpi_aml_log_crc = {
+ .buf = acpi_aml_log_buf,
+ .head = 0,
+ .tail = 0,
+};
+static const char *acpi_aml_file_path = ACPI_AML_FILE;
+static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
+static bool acpi_aml_exit;
+
+static bool acpi_aml_batch_drain;
+static unsigned long acpi_aml_batch_state;
+static char acpi_aml_batch_prompt;
+static char acpi_aml_batch_roll;
+static unsigned long acpi_aml_log_state;
+static char *acpi_aml_batch_cmd = NULL;
+static char *acpi_aml_batch_pos = NULL;
+
+static int acpi_aml_set_fl(int fd, int flags)
+{
+ int ret;
+
+ ret = fcntl(fd, F_GETFL, 0);
+ if (ret < 0) {
+ perror("fcntl(F_GETFL)");
+ return ret;
+ }
+ flags |= ret;
+ ret = fcntl(fd, F_SETFL, flags);
+ if (ret < 0) {
+ perror("fcntl(F_SETFL)");
+ return ret;
+ }
+ return ret;
+}
+
+static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
+{
+ if (fd > maxfd)
+ maxfd = fd;
+ FD_SET(fd, set);
+ return maxfd;
+}
+
+static int acpi_aml_read(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ len = read(fd, p, len);
+ if (len < 0)
+ perror("read");
+ else if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int remained = strlen(acpi_aml_batch_pos);
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ if (len > remained) {
+ memcpy(p, acpi_aml_batch_pos, remained);
+ acpi_aml_batch_pos += remained;
+ len = remained;
+ } else {
+ memcpy(p, acpi_aml_batch_pos, len);
+ acpi_aml_batch_pos += len;
+ }
+ if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int ret = 0;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
+ if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
+ *p = acpi_aml_batch_roll;
+ len = 1;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ } else {
+ len = read(fd, p, 1);
+ if (len <= 0) {
+ if (len < 0)
+ perror("read");
+ ret = len;
+ break;
+ }
+ }
+ switch (acpi_aml_log_state) {
+ case ACPI_AML_LOG_START:
+ if (*p == '\n')
+ acpi_aml_log_state = ACPI_AML_PROMPT_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ break;
+ case ACPI_AML_PROMPT_START:
+ if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
+ *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
+ acpi_aml_batch_prompt = *p;
+ acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
+ } else {
+ if (*p != '\n')
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ case ACPI_AML_PROMPT_STOP:
+ if (*p == ' ') {
+ acpi_aml_log_state = ACPI_AML_LOG_STOP;
+ acpi_aml_exit = true;
+ } else {
+ /* Roll back */
+ acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
+ acpi_aml_batch_roll = *p;
+ *p = acpi_aml_batch_prompt;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int acpi_aml_write(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ else if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ if (!acpi_aml_batch_drain) {
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ }
+ if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
+{
+ int len;
+
+ len = acpi_aml_write(fd, crc);
+ if (circ_count_to_end(crc) == 0)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ return len;
+}
+
+static void acpi_aml_loop(int fd)
+{
+ fd_set rfds;
+ fd_set wfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ if (acpi_aml_mode == ACPI_AML_BATCH) {
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ acpi_aml_batch_pos = acpi_aml_batch_cmd;
+ if (acpi_aml_batch_drain)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ else
+ acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
+ }
+ acpi_aml_exit = false;
+ while (!acpi_aml_exit) {
+ tv.tv_sec = ACPI_AML_SEC_TICK;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (acpi_aml_cmd_space()) {
+ if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
+ maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
+ else if (strlen(acpi_aml_batch_pos) &&
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
+ ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
+ }
+ if (acpi_aml_cmd_count() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
+ if (acpi_aml_log_space() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ if (acpi_aml_log_count())
+ maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
+
+ ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ if (ret < 0) {
+ perror("select");
+ break;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(STDIN_FILENO, &rfds))
+ ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
+ if (FD_ISSET(fd, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, write, cmd, ret);
+ else
+ ACPI_AML_DO(fd, write, cmd, ret);
+ }
+ if (FD_ISSET(fd, &rfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, read, log, ret);
+ else
+ ACPI_AML_DO(fd, read, log, ret);
+ }
+ if (FD_ISSET(STDOUT_FILENO, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
+ else
+ ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
+ }
+ }
+ }
+}
+
+static bool acpi_aml_readable(int fd)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = ACPI_AML_USEC_PEEK;
+ FD_ZERO(&rfds);
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
+ if (ret < 0)
+ perror("select");
+ if (ret > 0 && FD_ISSET(fd, &rfds))
+ return true;
+ return false;
+}
+
+/*
+ * This is a userspace IO flush implementation, replying on the prompt
+ * characters and can be turned into a flush() call after kernel implements
+ * .flush() filesystem operation.
+ */
+static void acpi_aml_flush(int fd)
+{
+ while (acpi_aml_readable(fd)) {
+ acpi_aml_batch_drain = true;
+ acpi_aml_loop(fd);
+ acpi_aml_batch_drain = false;
+ }
+}
+
+void usage(FILE *file, char *progname)
+{
+ fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
+ fprintf(file, "\nOptions:\n");
+ fprintf(file, " -b Specify command to be executed in batch mode\n");
+ fprintf(file, " -f Specify interface file other than");
+ fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n");
+ fprintf(file, " -h Print this help message\n");
+}
+
+int main(int argc, char **argv)
+{
+ int fd = 0;
+ int ch;
+ int len;
+ int ret = EXIT_SUCCESS;
+
+ while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
+ switch (ch) {
+ case 'b':
+ if (acpi_aml_batch_cmd) {
+ fprintf(stderr, "Already specify %s\n",
+ acpi_aml_batch_cmd);
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ len = strlen(optarg);
+ acpi_aml_batch_cmd = calloc(len + 2, 1);
+ if (!acpi_aml_batch_cmd) {
+ perror("calloc");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ memcpy(acpi_aml_batch_cmd, optarg, len);
+ acpi_aml_batch_cmd[len] = '\n';
+ acpi_aml_mode = ACPI_AML_BATCH;
+ break;
+ case 'f':
+ acpi_aml_file_path = optarg;
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ goto exit;
+ break;
+ case '?':
+ default:
+ usage(stderr, argv[0]);
+ ret = EXIT_FAILURE;
+ goto exit;
+ break;
+ }
+ }
+
+ fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ perror("open");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
+ acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
+
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ acpi_aml_flush(fd);
+ acpi_aml_loop(fd);
+
+exit:
+ if (fd < 0)
+ close(fd);
+ if (acpi_aml_batch_cmd)
+ free(acpi_aml_batch_cmd);
+ return ret;
+}
--
1.7.10

2015-11-10 08:22:31

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v2 7/7] ACPI / debugger: Add module support for ACPI debugger

This patch converts AML debugger into a loadable module.

Note that, it implements driver unloading at the level dependent on the
module reference count. Which means if ACPI debugger is being used by a
userspace program, "rmmod acpi_dbg" should result in failure.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 12 ++-
drivers/acpi/Makefile | 2 +-
drivers/acpi/acpi_dbg.c | 80 ++++++++++++------
drivers/acpi/bus.c | 3 +-
drivers/acpi/osl.c | 207 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/acpi.h | 71 ++++++++++++++++
include/linux/acpi_dbg.h | 52 ------------
7 files changed, 338 insertions(+), 89 deletions(-)
delete mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index e67b1b5..73b45f9 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -63,13 +63,23 @@ config ACPI_MEMORY_ACCESS_CHECK_SUPPORT
config ACPI_DEBUGGER
bool "In-kernel debugger"
select ACPI_DEBUG
- depends on DEBUG_FS
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.

+if ACPI_DEBUGGER
+
+config ACPI_DEBUGGER_USER
+ tristate "Userspace debugger accessiblity"
+ depends on DEBUG_FS
+ help
+ Export /sys/kernel/debug/acpi/acpidbg for userspace utilities
+ to access the debugger functionalities.
+
+endif
+
config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 102b5e6..c6f236f 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,7 +50,6 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
-acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
@@ -80,6 +79,7 @@ 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
+obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o

# processor has its own "processor." module_param namespace
processor-y := processor_driver.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
index 7be0851..8fcf752 100644
--- a/drivers/acpi/acpi_dbg.c
+++ b/drivers/acpi/acpi_dbg.c
@@ -21,7 +21,7 @@
#include <linux/proc_fs.h>
#include <linux/debugfs.h>
#include <linux/circ_buf.h>
-#include <linux/acpi_dbg.h>
+#include <linux/acpi.h>
#include "internal.h"

#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
@@ -307,7 +307,7 @@ static int acpi_aml_readb_kern(void)
* the debugger output and store the output into the debugger interface
* buffer. Return the size of stored logs or errno.
*/
-ssize_t acpi_aml_write_log(const char *msg)
+static ssize_t acpi_aml_write_log(const char *msg)
{
int ret = 0;
int count = 0, size = 0;
@@ -350,7 +350,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_write_log);

/*
* acpi_aml_read_cmd() - Capture debugger input
@@ -361,7 +360,7 @@ EXPORT_SYMBOL(acpi_aml_write_log);
* the debugger input commands and store the input commands into the
* debugger interface buffer. Return the size of stored commands or errno.
*/
-ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+static ssize_t acpi_aml_read_cmd(char *msg, size_t count)
{
int ret = 0;
int size = 0;
@@ -403,7 +402,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_read_cmd);

static int acpi_aml_thread(void *unsed)
{
@@ -440,7 +438,7 @@ static int acpi_aml_thread(void *unsed)
* This function should be used to implement acpi_os_execute() which is
* used by the ACPICA debugger to create the debugger thread.
*/
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+static int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
{
struct task_struct *t;

@@ -462,30 +460,27 @@ int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
mutex_unlock(&acpi_aml_io.lock);
return 0;
}
-EXPORT_SYMBOL(acpi_aml_create_thread);

-int acpi_aml_wait_command_ready(void)
+static int acpi_aml_wait_command_ready(bool single_step,
+ char *buffer, size_t length)
{
acpi_status status;

- if (!acpi_gbl_method_executing)
- acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
- else
+ if (single_step)
acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_get_line(buffer, length, NULL);
if (ACPI_FAILURE(status))
return -EINVAL;
return 0;
}
-EXPORT_SYMBOL(acpi_aml_wait_command_ready);

-int acpi_aml_notify_command_complete(void)
+static int acpi_aml_notify_command_complete(void)
{
return 0;
}
-EXPORT_SYMBOL(acpi_aml_notify_command_complete);

static int acpi_aml_open(struct inode *inode, struct file *file)
{
@@ -759,10 +754,23 @@ static const struct file_operations acpi_aml_operations = {
.llseek = generic_file_llseek,
};

+static const struct acpi_debugger_ops acpi_aml_debugger = {
+ .create_thread = acpi_aml_create_thread,
+ .read_cmd = acpi_aml_read_cmd,
+ .write_log = acpi_aml_write_log,
+ .wait_command_ready = acpi_aml_wait_command_ready,
+ .notify_command_complete = acpi_aml_notify_command_complete,
+};
+
int __init acpi_aml_init(void)
{
- if (!acpi_debugfs_dir)
- return -ENOENT;
+ int ret = 0;
+
+ if (!acpi_debugfs_dir) {
+ ret = -ENOENT;
+ goto err_exit;
+ }
+
/* Initialize AML IO interface */
mutex_init(&acpi_aml_io.lock);
init_waitqueue_head(&acpi_aml_io.wait);
@@ -772,21 +780,39 @@ int __init acpi_aml_init(void)
S_IFREG | S_IRUGO | S_IWUSR,
acpi_debugfs_dir, NULL,
&acpi_aml_operations);
- if (acpi_aml_dentry == NULL)
- return -ENODEV;
+ if (acpi_aml_dentry == NULL) {
+ ret = -ENODEV;
+ goto err_exit;
+ }
+ ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger);
+ if (ret)
+ goto err_fs;
acpi_aml_initialized = true;
- return 0;
+
+err_fs:
+ if (ret) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+err_exit:
+ return ret;
}

-#if 0
void __exit acpi_aml_exit(void)
{
- /* TODO: Stop the in kernel debugger */
- if (acpi_aml_dentry)
- debugfs_remove(acpi_aml_dentry);
- acpi_aml_initialized = false;
+ if (acpi_aml_initialized) {
+ acpi_unregister_debugger(&acpi_aml_debugger);
+ if (acpi_aml_dentry) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+ acpi_aml_initialized = false;
+ }
}

module_init(acpi_aml_init);
module_exit(acpi_aml_exit);
-#endif
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_DESCRIPTION("ACPI debugger userspace IO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 06fbba9..1a40111 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,7 +37,6 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
-#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1095,7 +1094,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
- acpi_aml_init();
+ acpi_debugger_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index bc3da95..17fb138 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,7 +40,6 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
-#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -220,6 +219,7 @@ void acpi_os_printf(const char *fmt, ...)
acpi_os_vprintf(fmt, args);
va_end(args);
}
+EXPORT_SYMBOL(acpi_os_printf);

void acpi_os_vprintf(const char *fmt, va_list args)
{
@@ -234,7 +234,7 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- if (acpi_aml_write_log(buffer) < 0)
+ if (acpi_debugger_write_log(buffer) < 0)
printk(KERN_CONT "%s", buffer);
#endif
}
@@ -1100,6 +1100,200 @@ static void acpi_os_execute_deferred(struct work_struct *work)
kfree(dpc);
}

+#ifdef CONFIG_ACPI_DEBUGGER
+static struct acpi_debugger acpi_debugger;
+static bool acpi_debugger_initialized;
+
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_debugger.lock);
+ if (acpi_debugger.ops) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+
+ acpi_debugger.owner = owner;
+ acpi_debugger.ops = ops;
+
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+EXPORT_SYMBOL(acpi_register_debugger);
+
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+ mutex_lock(&acpi_debugger.lock);
+ if (ops == acpi_debugger.ops) {
+ acpi_debugger.ops = NULL;
+ acpi_debugger.owner = NULL;
+ }
+ mutex_unlock(&acpi_debugger.lock);
+}
+EXPORT_SYMBOL(acpi_unregister_debugger);
+
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ int ret;
+ int (*func)(acpi_osd_exec_callback, void *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->create_thread;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(function, context);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_write_log(const char *msg)
+{
+ ssize_t ret;
+ ssize_t (*func)(const char *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->write_log;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(msg);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length)
+{
+ ssize_t ret;
+ ssize_t (*func)(char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->read_cmd;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(buffer, buffer_length);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_wait_command_ready(void)
+{
+ int ret;
+ int (*func)(bool, char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->wait_command_ready;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(acpi_gbl_method_executing,
+ acpi_gbl_db_line_buf, ACPI_DB_LINE_BUFFER_SIZE);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_notify_command_complete(void)
+{
+ int ret;
+ int (*func)(void);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->notify_command_complete;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func();
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int __init acpi_debugger_init(void)
+{
+ mutex_init(&acpi_debugger.lock);
+ acpi_debugger_initialized = true;
+ return 0;
+}
+#endif
+
/*******************************************************************************
*
* FUNCTION: acpi_os_execute
@@ -1127,7 +1321,7 @@ acpi_status acpi_os_execute(acpi_execute_type type,
function, context));

if (type == OSL_DEBUGGER_MAIN_THREAD) {
- ret = acpi_aml_create_thread(function, context);
+ ret = acpi_debugger_create_thread(function, context);
if (ret) {
pr_err("Call to kthread_create() failed.\n");
status = AE_ERROR;
@@ -1377,7 +1571,7 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
#else
int ret;

- ret = acpi_aml_read_cmd(buffer, buffer_length);
+ ret = acpi_debugger_read_cmd(buffer, buffer_length);
if (ret < 0)
return AE_ERROR;
if (bytes_read)
@@ -1386,6 +1580,7 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)

return AE_OK;
}
+EXPORT_SYMBOL(acpi_os_get_line);

#ifdef CONFIG_ACPI_MEMORY_ACCESS_CHECK_SUPPORT
u8 acpi_os_readable(void *pointer, acpi_size length)
@@ -1403,7 +1598,7 @@ acpi_status acpi_os_wait_command_ready(void)
{
int ret;

- ret = acpi_aml_wait_command_ready();
+ ret = acpi_debugger_wait_command_ready();
if (ret < 0)
return AE_ERROR;
return AE_OK;
@@ -1413,7 +1608,7 @@ acpi_status acpi_os_notify_command_complete(void)
{
int ret;

- ret = acpi_aml_notify_command_complete();
+ ret = acpi_debugger_notify_command_complete();
if (ret < 0)
return AE_ERROR;
return AE_OK;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 51a96a8..8f24e03 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -37,6 +37,8 @@
#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/dynamic_debug.h>
+#include <linux/module.h>
+#include <linux/mutex.h>

#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -119,6 +121,75 @@ typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
typedef int (*acpi_tbl_entry_handler)(struct acpi_subtable_header *header,
const unsigned long end);

+/* Debugger support */
+
+struct acpi_debugger_ops {
+ int (*create_thread)(acpi_osd_exec_callback function, void *context);
+ ssize_t (*write_log)(const char *msg);
+ ssize_t (*read_cmd)(char *buffer, size_t length);
+ int (*wait_command_ready)(bool single_step, char *buffer, size_t length);
+ int (*notify_command_complete)(void);
+};
+
+struct acpi_debugger {
+ const struct acpi_debugger_ops *ops;
+ struct module *owner;
+ struct mutex lock;
+};
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_debugger_init(void);
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops);
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops);
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_debugger_write_log(const char *msg);
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length);
+int acpi_debugger_wait_command_ready(void);
+int acpi_debugger_notify_command_complete(void);
+#else
+static inline int acpi_debugger_init(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ return -ENODEV;
+}
+
+static inline void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+}
+
+static inline int acpi_debugger_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size);
#else
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
deleted file mode 100644
index 60f3887..0000000
--- a/include/linux/acpi_dbg.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * ACPI AML interfacing support
- *
- * Copyright (C) 2015, Intel Corporation
- * Authors: Lv Zheng <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _LINUX_ACPI_DBG_H
-#define _LINUX_ACPI_DBG_H
-
-#include <linux/acpi.h>
-
-#ifdef CONFIG_ACPI_DEBUGGER
-int __init acpi_aml_init(void);
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
-ssize_t acpi_aml_write_log(const char *msg);
-ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
-int acpi_aml_wait_command_ready(void);
-int acpi_aml_notify_command_complete(void);
-#else
-static int inline acpi_aml_init(void)
-{
- return 0;
-}
-static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
- void *context)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_write_log(const char *msg)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_wait_command_ready(void)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_notify_command_complete(void)
-{
- return -ENODEV;
-}
-#endif
-
-#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-11-10 09:42:47

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support

Hi, Lv
Sorry for my late feedback on the patch, one minor nit below:

> -----Original Message-----
> From: Zheng, Lv
> Sent: Tuesday, November 10, 2015 4:22 PM
> To: Wysocki, Rafael J; Brown, Len
> Cc: Zheng, Lv; Lv Zheng; [email protected]; linux-
> [email protected]; Chen, Yu C
> Subject: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support
>
> From: Chen Yu <[email protected]>
>
> This patch implements acpi_os_readable(). The function is used by ACPI AML
> debugger to validate user specified pointers for the dumpable AML operand
> objects.
>
> Signed-off-by: Chen Yu <[email protected]>
> Signed-off-by: Lv Zheng <[email protected]>
> ---
> +bool __acpi_memory_readable(void *pointer, size_t length) {
> + unsigned long start_pfn, end_pfn;
> +
We should only check the low memory, otherwise the highmem address might
bring unexpected behavior in this function, I think we should add:
if (pointer >= PAGE_OFFSET)
return false;
> + start_pfn = page_to_pfn(virt_to_page(pointer));
> + end_pfn = page_to_pfn(virt_to_page(pointer + length));
Convert pointer to char* first?
end_pfn = page_to_pfn(virt_to_page((char*)pointer + length));

> + /*
> + * Since the acpi address is allocated by kmalloc, we only
> + * need to consider the direct-mapping virtual address,
> + * rather than the kmap/vmalloc/ioremap address.
> + */
> + return pfn_range_is_mapped(start_pfn, end_pfn) ? true : false; }
> +
Thanks!

Best Regards,
Yu

2015-11-10 10:47:25

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support


> -----Original Message-----
> From: Chen, Yu C
> Sent: Tuesday, November 10, 2015 5:43 PM
> To: Zheng, Lv
> Cc: Lv Zheng; [email protected]; [email protected];
> Wysocki, Rafael J; Brown, Len
> Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support
>
> Hi, Lv
> Sorry for my late feedback on the patch, one minor nit below:
>
> > -----Original Message-----
> > From: Zheng, Lv
> > Sent: Tuesday, November 10, 2015 4:22 PM
> > To: Wysocki, Rafael J; Brown, Len
> > Cc: Zheng, Lv; Lv Zheng; [email protected]; linux-
> > [email protected]; Chen, Yu C
> > Subject: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable()
> > support
> >
> > From: Chen Yu <[email protected]>
> >
> > This patch implements acpi_os_readable(). The function is used by ACPI
> > AML debugger to validate user specified pointers for the dumpable AML
> > operand objects.
> >
> > Signed-off-by: Chen Yu <[email protected]>
> > Signed-off-by: Lv Zheng <[email protected]>
> > ---
> > +bool __acpi_memory_readable(void *pointer, size_t length) {
> > + unsigned long start_pfn, end_pfn;
> > +
> We should only check the low memory, otherwise the highmem address
> might bring unexpected behavior in this function, I think we should add:
> if (pointer >= PAGE_OFFSET)
> return false;
Should be :
If (pointer < PAGE_OFFSET && pointer >= high_memory)
return false;
> > + start_pfn = page_to_pfn(virt_to_page(pointer));
> > + end_pfn = page_to_pfn(virt_to_page(pointer + length));
> Convert pointer to char* first?
> end_pfn = page_to_pfn(virt_to_page((char*)pointer + length));
>
> > + /*
> > + * Since the acpi address is allocated by kmalloc, we only
> > + * need to consider the direct-mapping virtual address,
> > + * rather than the kmap/vmalloc/ioremap address.
> > + */
> > + return pfn_range_is_mapped(start_pfn, end_pfn) ? true : false; }
> > +
> Thanks!
>
> Best Regards,
> Yu

2015-11-10 13:04:31

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support

On Tue, Nov 10, 2015 at 11:42 AM, Chen, Yu C <[email protected]> wrote:
> Hi, Lv
> Sorry for my late feedback on the patch, one minor nit below:

>> + start_pfn = page_to_pfn(virt_to_page(pointer));
>> + end_pfn = page_to_pfn(virt_to_page(pointer + length));
> Convert pointer to char* first?
> end_pfn = page_to_pfn(virt_to_page((char*)pointer + length));

Why do you need that?
void * + size_t = char * + size_t

--
With Best Regards,
Andy Shevchenko

2015-11-10 13:56:56

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support

Hi, Shevchenko,
Thanks for you review

> -----Original Message-----
> From: Andy Shevchenko [mailto:[email protected]]
> Sent: Tuesday, November 10, 2015 9:04 PM
> To: Chen, Yu C
> Cc: Zheng, Lv; Lv Zheng; [email protected]; linux-
> [email protected]; Wysocki, Rafael J; Brown, Len
> Subject: Re: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support
>
> On Tue, Nov 10, 2015 at 11:42 AM, Chen, Yu C <[email protected]> wrote:
> > Hi, Lv
> > Sorry for my late feedback on the patch, one minor nit below:
>
> >> + start_pfn = page_to_pfn(virt_to_page(pointer));
> >> + end_pfn = page_to_pfn(virt_to_page(pointer + length));
> > Convert pointer to char* first?
> > end_pfn = page_to_pfn(virt_to_page((char*)pointer + length));
>
> Why do you need that?
> void * + size_t = char * + size_t
>
Hum, I was under impression it was a pointer, so I converted it to char* for safety,
but it seems a wrong impression,and I've just tested it on gcc, it is OK without
char*.

> --
> With Best Regards,
> Andy Shevchenko
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-11-11 05:06:44

by Zheng, Lv

[permalink] [raw]
Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support

Hi, Yu

> From: Chen, Yu C
> Sent: Tuesday, November 10, 2015 5:43 PM
>
> Hi, Lv
> Sorry for my late feedback on the patch, one minor nit below:
>
> > From: Zheng, Lv
> > Sent: Tuesday, November 10, 2015 4:22 PM
> >
> > From: Chen Yu <[email protected]>
> >
> > This patch implements acpi_os_readable(). The function is used by ACPI AML
> > debugger to validate user specified pointers for the dumpable AML operand
> > objects.
> >
> > Signed-off-by: Chen Yu <[email protected]>
> > Signed-off-by: Lv Zheng <[email protected]>
> > ---
> > +bool __acpi_memory_readable(void *pointer, size_t length) {
> > + unsigned long start_pfn, end_pfn;
> > +
> We should only check the low memory, otherwise the highmem address might
> bring unexpected behavior in this function, I think we should add:
> if (pointer >= PAGE_OFFSET)
> return false;

I think pfn_range_is_mapped() can ensure this.
If you want to add some sanity checks, IMO, virt_addr_valid() could be better.

> > + start_pfn = page_to_pfn(virt_to_page(pointer));
> > + end_pfn = page_to_pfn(virt_to_page(pointer + length));
> Convert pointer to char* first?
> end_pfn = page_to_pfn(virt_to_page((char*)pointer + length));

Either a compiler supports such a conversion or a compiler ends up fatal error by complaining unknown sizeof(void *).
So there is no need to do this here as the build test has passed.

Thanks and best regards
-Lv

>
> > + /*
> > + * Since the acpi address is allocated by kmalloc, we only
> > + * need to consider the direct-mapping virtual address,
> > + * rather than the kmap/vmalloc/ioremap address.
> > + */
> > + return pfn_range_is_mapped(start_pfn, end_pfn) ? true : false; }
> > +
> Thanks!
>
> Best Regards,
> Yu

2015-11-11 05:28:09

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support

Hi,

> -----Original Message-----
> From: Zheng, Lv
> Sent: Wednesday, November 11, 2015 1:07 PM
> To: Chen, Yu C
> Cc: Lv Zheng; [email protected]; [email protected];
> Wysocki, Rafael J; Brown, Len
> Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support
>
> Hi, Yu
>
> > From: Chen, Yu C
> > Sent: Tuesday, November 10, 2015 5:43 PM
> >
> > Hi, Lv
> > Sorry for my late feedback on the patch, one minor nit below:
> >
> > > From: Zheng, Lv
> > > Sent: Tuesday, November 10, 2015 4:22 PM
> > >
> > > From: Chen Yu <[email protected]>
> > >
> > > This patch implements acpi_os_readable(). The function is used by
> > > ACPI AML debugger to validate user specified pointers for the
> > > dumpable AML operand objects.
> > >
> > > Signed-off-by: Chen Yu <[email protected]>
> > > Signed-off-by: Lv Zheng <[email protected]>
> > > ---
> > > +bool __acpi_memory_readable(void *pointer, size_t length) {
> > > + unsigned long start_pfn, end_pfn;
> > > +
> > We should only check the low memory, otherwise the highmem address
> > might bring unexpected behavior in this function, I think we should add:
> > if (pointer >= PAGE_OFFSET)
> > return false;
>
> I think pfn_range_is_mapped() can ensure this.
virt_to_page might return an invalid page struct if pointer is not a valid kernel address.
> If you want to add some sanity checks, IMO, virt_addr_valid() could be
> better.
Yes, virt_addr_valid is better.
>
> > > + start_pfn = page_to_pfn(virt_to_page(pointer));
> > > + end_pfn = page_to_pfn(virt_to_page(pointer + length));
> > Convert pointer to char* first?
> > end_pfn = page_to_pfn(virt_to_page((char*)pointer + length));
>
> Either a compiler supports such a conversion or a compiler ends up fatal error
> by complaining unknown sizeof(void *).
> So there is no need to do this here as the build test has passed.
>
OK, got it, thanks.

> Thanks and best regards
> -Lv
>
> >
> > > + /*
> > > + * Since the acpi address is allocated by kmalloc, we only
> > > + * need to consider the direct-mapping virtual address,
> > > + * rather than the kmap/vmalloc/ioremap address.
> > > + */
> > > + return pfn_range_is_mapped(start_pfn, end_pfn) ? true : false; }
> > > +
> > Thanks!
> >
> > Best Regards,
> > Yu

2015-11-17 01:49:42

by Zheng, Lv

[permalink] [raw]
Subject: RE: [PATCH v2 5/7] ACPI / x86: introduce acpi_os_readable() support

Hi, Yu

> From: Zheng, Lv
> Sent: Wednesday, November 11, 2015 1:07 PM
>
> Hi, Yu
>
> > From: Chen, Yu C
> > Sent: Tuesday, November 10, 2015 5:43 PM
> >
> > Hi, Lv
> > Sorry for my late feedback on the patch, one minor nit below:
> >
> > > From: Zheng, Lv
> > > Sent: Tuesday, November 10, 2015 4:22 PM
> > >
> > > From: Chen Yu <[email protected]>
> > >
> > > This patch implements acpi_os_readable(). The function is used by ACPI AML
> > > debugger to validate user specified pointers for the dumpable AML operand
> > > objects.
> > >
> > > Signed-off-by: Chen Yu <[email protected]>
> > > Signed-off-by: Lv Zheng <[email protected]>
> > > ---
> > > +bool __acpi_memory_readable(void *pointer, size_t length) {
> > > + unsigned long start_pfn, end_pfn;
> > > +
> > We should only check the low memory, otherwise the highmem address might
> > bring unexpected behavior in this function, I think we should add:
> > if (pointer >= PAGE_OFFSET)
> > return false;
>
> I think pfn_range_is_mapped() can ensure this.
> If you want to add some sanity checks, IMO, virt_addr_valid() could be better.

I confirmed that pfn_range_is_mapped() doesn't cover this for us.
We need such a check or enhance this in pfn_range_is_mapped().
I'll remove this patch from this patchset and re-send the rest of the patches in this patchset as v3.
For acpi_os_readable() support, I'll create a separate RFC patchset with different approaches included.

Thanks and best regards
-Lv

>
> > > + start_pfn = page_to_pfn(virt_to_page(pointer));
> > > + end_pfn = page_to_pfn(virt_to_page(pointer + length));
> > Convert pointer to char* first?
> > end_pfn = page_to_pfn(virt_to_page((char*)pointer + length));
>
> Either a compiler supports such a conversion or a compiler ends up fatal error by complaining unknown sizeof(void *).
> So there is no need to do this here as the build test has passed.
>
> Thanks and best regards
> -Lv
>
> >
> > > + /*
> > > + * Since the acpi address is allocated by kmalloc, we only
> > > + * need to consider the direct-mapping virtual address,
> > > + * rather than the kmap/vmalloc/ioremap address.
> > > + */
> > > + return pfn_range_is_mapped(start_pfn, end_pfn) ? true : false; }
> > > +
> > Thanks!
> >
> > Best Regards,
> > Yu

2015-11-19 06:08:20

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3 0/6] ACPICA / debugger: Add in-kernel AML debugger support

This patchset enables ACPICA debugger for Linux kernel and implements a
userspace utility to access it.

A. Build the AML debugger
In order to build the kernel support of AML debugger, the following kconfig
items should be enabled:
CONFIG_ACPI_DEBUG=y
CONFIG_ACPI_DEBUGGER=y
CONFIG_DEBUG_FS=y
CONFIG_ACPI_DEBUGGER_USER=m
The userspace tool can be found at tools/power/acpi/tools/acpidbg. To
build this utility, staying in tools folder and type "make acpi".

B. Load the AML debugger during runtime
In order to use the in-kernel AML debugger, the following command should be
executed using root user:
# modprobe acpi_dbg
# mount -t debugfs none /sys/kernel/debug
# acpidbg

C. Batch mode
In order to support scripts, the userspace utility also supports single
command batch mode:
# acpidbg -b "help"
# acpidbg -b "tables"
# acpidbg -b "find _LID"
# acpidbg -b "execute \_SB.LID0._LID"
You can find the documentation about the ACPICA debugger commands in:
https://acpica.org/sites/acpica/files/acpica-reference_17.pdf
(The latest document can be found at https://acpica.org/documentation)
And refer to the chapter - ACPICA debugger reference to obtain the full
description of the debugger commands. Note that not all commands are
supported by an in-kernel AML debugger.

D. Unload the AML debugger during runtime
After terminating all acpidbg instances, the following command can be
executed to remove the AML debugger from kernel:
# rmmod acpi_dbg

The following tasks are not completed:
1. .flush() support in the kernel debugger IO driver.
2. multi-commands batch mode.

v2:
1. Fix a help message issue for the userspace acpidbg utility (this is a
typo fix).
2. Fix a batch mode issue by adding "\n" to the prompt sequence (this was
lost due to too many rebase cycles).
3. Fix kconfig dependency for an intermediate patch (this is only useful in
case the latter patches are reverted).
4. Add more accurate runtime debugger stub mechanism and remove old
ACPI_DEBUGGER_EXEC() stub mechanism.
5. Add x86 acpi_os_readable() support.

v3:
1. Remove acpi_os_readable() support from this patchset. It's sent separately.
2. Delete some deprecated comments.

Colin Ian King (1):
ACPICA: Debugger: Remove unnecessary status check

Lv Zheng (5):
ACPICA: Debugger: Convert some mechanisms to OSPM specific
ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using
different stub mechanism
ACPI / debugger: Add IO interface to access debugger functionalities
tools/power/acpi: Add userspace AML interface support
ACPI / debugger: Add module support for ACPI debugger

drivers/acpi/Kconfig | 13 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 805 ++++++++++++++++++++++++++++++
drivers/acpi/acpica/acdebug.h | 36 +-
drivers/acpi/acpica/acglobal.h | 5 -
drivers/acpi/acpica/acmacros.h | 11 -
drivers/acpi/acpica/dbdisply.c | 12 +
drivers/acpi/acpica/dbinput.c | 100 +---
drivers/acpi/acpica/dbxface.c | 93 ++--
drivers/acpi/acpica/dscontrol.c | 10 +-
drivers/acpi/acpica/dsutils.c | 16 +-
drivers/acpi/acpica/dswexec.c | 16 +-
drivers/acpi/acpica/utmutex.c | 17 -
drivers/acpi/bus.c | 1 +
drivers/acpi/osl.c | 250 +++++++++-
include/acpi/acpiosxf.h | 18 +-
include/acpi/acpixf.h | 34 ++
include/acpi/platform/aclinux.h | 2 +
include/acpi/platform/aclinuxex.h | 9 +
include/linux/acpi.h | 71 +++
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 +
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++
23 files changed, 1792 insertions(+), 209 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

--
1.7.10

2015-11-19 06:08:28

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3 1/6] ACPICA: Debugger: Remove unnecessary status check

From: Colin Ian King <[email protected]>

ACPICA commit f9d5c6c9a25e9f5ac05458bfcd8b381e21bb2ba5

ACPICA BZ 1205. Colin Ian King.

Link: https://bugs.acpica.org/show_bug.cgi?id=1205
Link: https://github.com/acpica/acpica/commit/f9d5c6c9
Signed-off-by: Colin Ian King <[email protected]>
Signed-off-by: Bob Moore <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/dbinput.c | 3 ---
1 file changed, 3 deletions(-)

diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index 0480254..fe93f67 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -1246,9 +1246,6 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
* 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,
--
1.7.10

2015-11-19 06:08:35

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3 2/6] ACPICA: Debugger: Convert some mechanisms to OSPM specific

The following mechanisms are OSPM specific:
1. Redirect output destination to console: no file redirection will be
needed by an in-kernel debugger, there is even no file can be accessed
when the debugger is running in the kernel mode.
2. Output command prompts: programs other than acpiexec can have different
prompt characters and the prompt characters may be implemented as a
special character sequence to form a char device IO protocol.
3. Command ready/complete handshake: OSPM debugger may wait more conditions
to implement OSPM specific semantics (for example, FIFO full/empty
conditions for O_NONBLOCK or IO open/close conditions).
Leaving such OSPM specific stuffs in the ACPICA debugger core blocks
Linux debugger IO driver implementation.

Several new OSL APIs are provided by this patch:
1. acpi_os_initialize_command_signals: initialize command handshake mechanism
or any other OSPM specific stuffs.
2. acpi_os_terminate_command_signals: reversal of
acpi_os_initialize_command_signals.
3. acpi_os_wait_command_ready: putting debugger task into wait state when a
command is not ready. OSPMs can terminate command loop by returning
AE_CTRL_TERMINATE from this API. Normally, wait_event() or
wait_for_multiple_object() may be used to implement this API.
4. acpi_os_notify_command_complete: putting user task into running state when a
command has been completed. OSPMs can terminate command loop by
returning AE_CTRL_TERMINATE from this API. Normally, wake_up() or
set_event() may be used to implement this API.
This patch also converts current command signaling implementation into a
generic debugger layer (osgendbg.c) to be used by the existing OSPMs or
acpiexec, in return, Linux can have chance to implement its own command
handshake mechanism. This patch also implements acpiexec batch mode in a
multi-threading mode comaptible style as a demo (this can be confirmed by
configuring acpiexec into DEBUGGER_MULTI_THREADED mode where the batch mode
is still working). Lv Zheng.

Note that the OSPM specific command handshake mechanism is required by
Linux kernel because:
1. Linux kernel trends to use wait queue to synchronize two threads, using
mutexes to achieve that will cause false "dead lock" warnings.
2. The command handshake mechanism implemented by ACPICA is implemented in
this way because of a design issue in debugger IO streaming. Debugger IO
outputs are simply cached using a giant buffer, this should be tuned by
Linux in the future.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/acdebug.h | 2 +-
drivers/acpi/acpica/acglobal.h | 5 --
drivers/acpi/acpica/dbinput.c | 97 ++++++++-----------------------------
drivers/acpi/acpica/dbxface.c | 63 +++++++-----------------
drivers/acpi/acpica/utmutex.c | 17 -------
include/acpi/acpiosxf.h | 18 ++++++-
include/acpi/acpixf.h | 11 +++++
include/acpi/platform/aclinux.h | 4 ++
include/acpi/platform/aclinuxex.h | 19 ++++++++
9 files changed, 89 insertions(+), 147 deletions(-)

diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index c928ba4..86474d8 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -257,7 +257,7 @@ acpi_db_command_dispatch(char *input_buffer,

void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context);

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op);
+acpi_status acpi_db_user_commands(void);

char *acpi_db_get_next_token(char *string,
char **next, acpi_object_type * return_type);
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index faa9760..3977134 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -326,7 +326,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list);
#ifdef ACPI_DEBUGGER

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);
@@ -345,7 +344,6 @@ ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]);

/* These buffers should all be the same size */

-ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]);
@@ -360,9 +358,6 @@ 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/dbinput.c b/drivers/acpi/acpica/dbinput.c
index fe93f67..2bf8e6b 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -53,8 +53,6 @@ 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);
@@ -1149,55 +1147,16 @@ acpi_db_command_dispatch(char *input_buffer,

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_db_terminate_loop) {
- 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);
- }
+ (void)acpi_db_user_commands();
acpi_gbl_db_threads_terminated = TRUE;
}

/*******************************************************************************
*
- * 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
+ * PARAMETERS: None
*
* RETURN: None
*
@@ -1206,7 +1165,7 @@ static void acpi_db_single_thread(void)
*
******************************************************************************/

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
+acpi_status acpi_db_user_commands(void)
{
acpi_status status = AE_OK;

@@ -1216,49 +1175,31 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)

while (!acpi_gbl_db_terminate_loop) {

- /* 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 */
+ /* Wait the readiness of the command */

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_wait_command_ready();
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "While parsing command line"));
- return (status);
+ break;
}

- /* Check for single or multithreaded debug */
+ /* Just call to the command line interpreter */

- 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);
+ acpi_gbl_method_executing = FALSE;
+ acpi_gbl_step_to_next_call = FALSE;

- 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 */
+ (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL,
+ NULL);
+
+ /* Notify the completion of the command */

- acpi_db_single_thread();
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ break;
}
}

+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status, "While parsing command line"));
+ }
return (status);
}
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index 342298a..d95e91f 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -85,46 +85,21 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

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);
+ while (status == AE_CTRL_TRUE) {

- /* Different prompt if method is executing */
+ /* Notify the completion of the command */

- if (!acpi_gbl_method_executing) {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_COMMAND_PROMPT);
- } else {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }

- /* Get the user input line */
+ /* Wait the readiness of the command */

- 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_os_wait_command_ready();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
}

status =
@@ -134,6 +109,11 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */

+error_exit:
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While parsing/handling command line"));
+ }
return (status);
}

@@ -420,15 +400,7 @@ acpi_status acpi_initialize_debugger(void)

/* 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);
+ status = acpi_os_initialize_command_signals();
if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get debugger mutex\n");
return_ACPI_STATUS(status);
@@ -473,13 +445,14 @@ void acpi_terminate_debugger(void)
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);
}
+
+ acpi_os_terminate_command_signals();
}

if (acpi_gbl_db_buffer) {
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index ce406e3..ea0c207 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -111,17 +111,6 @@ acpi_status acpi_ut_mutex_initialize(void)
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);
}
@@ -162,12 +151,6 @@ 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/acpiosxf.h b/include/acpi/acpiosxf.h
index fbc2baf..0d824a2 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -349,12 +349,28 @@ void acpi_os_redirect_output(void *destination);
#endif

/*
- * Debug input
+ * Debug IO
*/
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line
acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read);
#endif

+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+acpi_status acpi_os_initialize_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+void acpi_os_terminate_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+acpi_status acpi_os_wait_command_ready(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete
+acpi_status acpi_os_notify_command_complete(void);
+#endif
+
/*
* Obtain ACPI table(s)
*/
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 3aaaa86..95ebae3 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -264,6 +264,15 @@ ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0);

/*
+ * Debugger command handshake globals. Host OSes need to access these
+ * variables to implement their own command handshake mechanism.
+ */
+#ifdef ACPI_DEBUGGER
+ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE);
+ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
+#endif
+
+/*
* Other miscellaneous globals
*/
ACPI_GLOBAL(struct acpi_table_fadt, acpi_gbl_FADT);
@@ -929,6 +938,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
void **data,
void (*callback)(void *)))

+void acpi_run_debugger(char *batch_buffer);
+
void acpi_set_debugger_thread_id(acpi_thread_id thread_id);

#endif /* __ACXFACE_H__ */
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 323e5da..69dbae6 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -150,6 +150,10 @@
*/
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index fd6d70f..673fdf4 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -129,6 +129,25 @@ static inline u8 acpi_os_readable(void *pointer, acpi_size length)
return TRUE;
}

+static inline acpi_status acpi_os_initialize_command_signals(void)
+{
+ return AE_OK;
+}
+
+static inline void acpi_os_terminate_command_signals(void)
+{
+}
+
+static inline acpi_status acpi_os_wait_command_ready(void)
+{
+ return AE_ERROR;
+}
+
+static inline acpi_status acpi_os_notify_command_complete(void)
+{
+ return AE_ERROR;
+}
+
/*
* OSL interfaces added by Linux
*/
--
1.7.10

2015-11-19 06:08:42

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3 3/6] ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using different stub mechanism

The ACPI_DEBUGGER_EXEC is a problem now when the debugger code is compiled
but runtime disabled. They actually will get executed in this situation.
Although such executions are harmless if we can correctly make
acpi_db_single_step() a runtime stub, users may still do not want to see the
debugger print messages logged into OSPMs' kernel logs when a debugger
driver is not loaded to enable the debugger during runtime.

This patch fixes this issue by introducing new stub mechanism instead of
ACPI_DEBUGGER_EXEC. Lv Zheng.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/acdebug.h | 34 ++++++++++++++++++++++++----------
drivers/acpi/acpica/acmacros.h | 11 -----------
drivers/acpi/acpica/dbdisply.c | 12 ++++++++++++
drivers/acpi/acpica/dbxface.c | 30 ++++++++++++++++++++++++++++++
drivers/acpi/acpica/dscontrol.c | 10 ++--------
drivers/acpi/acpica/dsutils.c | 16 ++++++++--------
drivers/acpi/acpica/dswexec.c | 16 +++++++---------
include/acpi/acpixf.h | 23 +++++++++++++++++++++++
8 files changed, 106 insertions(+), 46 deletions(-)

diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index 86474d8..dcaa15d 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -80,9 +80,15 @@ struct acpi_db_execute_walk {
/*
* dbxface - external debugger interfaces
*/
-acpi_status
-acpi_db_single_step(struct acpi_walk_state *walk_state,
- union acpi_parse_object *op, u32 op_type);
+ACPI_DBR_DEPENDENT_RETURN_OK(acpi_status
+ acpi_db_single_step(struct acpi_walk_state
+ *walk_state,
+ union acpi_parse_object *op,
+ u32 op_type))
+ ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_signal_break_point(struct
+ acpi_walk_state
+ *walk_state))

/*
* dbcmds - debug commands and output routines
@@ -182,11 +188,15 @@ void acpi_db_display_method_info(union acpi_parse_object *op);

void acpi_db_decode_and_display_object(char *target, char *output_type);

-void
-acpi_db_display_result_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_result_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))

-acpi_status acpi_db_display_all_methods(char *display_count_arg);
+ acpi_status acpi_db_display_all_methods(char *display_count_arg);

void acpi_db_display_arguments(void);

@@ -198,9 +208,13 @@ void acpi_db_display_calling_tree(void);

void acpi_db_display_object_type(char *object_arg);

-void
-acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_argument_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))

/*
* dbexec - debugger control method execution
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index e85366c..bad5bca 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -401,17 +401,6 @@
#endif

/*
- * Some code only gets executed when the debugger is built in.
- * Note that this is entirely independent of whether the
- * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not.
- */
-#ifdef ACPI_DEBUGGER
-#define ACPI_DEBUGGER_EXEC(a) a
-#else
-#define ACPI_DEBUGGER_EXEC(a)
-#endif
-
-/*
* Macros used for ACPICA utilities only
*/

diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c
index 672977e..c42ce8a 100644
--- a/drivers/acpi/acpica/dbdisply.c
+++ b/drivers/acpi/acpica/dbdisply.c
@@ -679,6 +679,12 @@ acpi_db_display_result_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{

+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
/* Only display if single stepping */

if (!acpi_gbl_cm_single_step) {
@@ -708,6 +714,12 @@ acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{

+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
if (!acpi_gbl_cm_single_step) {
return;
}
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index d95e91f..d7ff58e 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -119,6 +119,36 @@ error_exit:

/*******************************************************************************
*
+ * FUNCTION: acpi_db_signal_break_point
+ *
+ * PARAMETERS: walk_state - Current walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called for AML_BREAK_POINT_OP
+ *
+ ******************************************************************************/
+
+void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)
+{
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
+ /*
+ * Set the single-step flag. This will cause the debugger (if present)
+ * to break to the console within the AML debugger at the start of the
+ * next AML instruction.
+ */
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_os_printf("**break** Executed AML BreakPoint opcode\n");
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_db_single_step
*
* PARAMETERS: walk_state - Current walk
diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c
index 435fc16..06a6f7f 100644
--- a/drivers/acpi/acpica/dscontrol.c
+++ b/drivers/acpi/acpica/dscontrol.c
@@ -47,6 +47,7 @@
#include "amlcode.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "acdebug.h"

#define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME("dscontrol")
@@ -348,14 +349,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state,

case AML_BREAK_POINT_OP:

- /*
- * Set the single-step flag. This will cause the debugger (if present)
- * to break to the console within the AML debugger at the start of the
- * next AML instruction.
- */
- ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE);
- ACPI_DEBUGGER_EXEC(acpi_os_printf
- ("**break** Executed AML BreakPoint opcode\n"));
+ acpi_db_signal_break_point(walk_state);

/* Call to the OSL in case OS wants a piece of the action */

diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index ebc577b..e4293a8 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -605,8 +605,8 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+
+ acpi_db_display_argument_object(obj_desc, walk_state);
} else {
/* Check for null name case */

@@ -638,10 +638,11 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"Argument previously created, already stacked\n"));

- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (walk_state->
- operands[walk_state->num_operands -
- 1], walk_state));
+ acpi_db_display_argument_object(walk_state->
+ operands[walk_state->
+ num_operands -
+ 1],
+ walk_state);

/*
* Use value that was already previously returned
@@ -685,8 +686,7 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(status);
}

- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+ acpi_db_display_argument_object(obj_desc, walk_state);
}

return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index df54d46..9cc5761 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -178,8 +178,7 @@ cleanup:

/* Break to debugger to display result */

- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (local_obj_desc, walk_state));
+ acpi_db_display_result_object(local_obj_desc, walk_state);

/*
* Delete the predicate result object (we know that
@@ -386,11 +385,10 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)

/* Call debugger for single step support (DEBUG build only) */

- ACPI_DEBUGGER_EXEC(status =
- acpi_db_single_step(walk_state, op, op_class));
- ACPI_DEBUGGER_EXEC(if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);}
- ) ;
+ status = acpi_db_single_step(walk_state, op, op_class);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }

/* Decode the Opcode Class */

@@ -728,8 +726,8 @@ cleanup:

/* Break to debugger to display result */

- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (walk_state->result_obj, walk_state));
+ acpi_db_display_result_object(walk_state->result_obj,
+ walk_state);

/*
* Delete the result op if and only if:
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 95ebae3..5dfab9c 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -375,6 +375,29 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);

#endif /* ACPI_APPLICATION */

+/*
+ * Debugger prototypes
+ *
+ * All interfaces used by debugger will be configured
+ * out of the ACPICA build unless the ACPI_DEBUGGER
+ * flag is defined.
+ */
+#ifdef ACPI_DEBUGGER
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ ACPI_EXTERNAL_RETURN_OK(prototype)
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ ACPI_EXTERNAL_RETURN_VOID(prototype)
+
+#else
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ static ACPI_INLINE prototype {return(AE_OK);}
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ static ACPI_INLINE prototype {return;}
+
+#endif /* ACPI_DEBUGGER */
+
/*****************************************************************************
*
* ACPICA public interface prototypes
--
1.7.10

2015-11-19 06:08:53

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3 4/6] ACPI / debugger: Add IO interface to access debugger functionalities

This patch adds /sys/kernel/debug/acpi/acpidbg, which can be used by
userspace programs to access ACPICA debugger functionalities.

Known issue:
1. IO flush support
acpi_os_notify_command_complete() and acpi_os_wait_command_ready() can
be used by acpi_dbg module to implement .flush() filesystem operation.
While this patch doesn't go that far. It then becomes userspace tool's
duty now to flush old commands before executing new batch mode commands.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 3 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 779 +++++++++++++++++++++++++++++++++++++
drivers/acpi/bus.c | 2 +
drivers/acpi/osl.c | 55 ++-
include/acpi/platform/aclinux.h | 2 -
include/acpi/platform/aclinuxex.h | 10 -
include/linux/acpi_dbg.h | 52 +++
8 files changed, 887 insertions(+), 17 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 25dbb76..4de3517 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -58,8 +58,9 @@ config ACPI_CCA_REQUIRED
bool

config ACPI_DEBUGGER
- bool "In-kernel debugger (EXPERIMENTAL)"
+ bool "In-kernel debugger"
select ACPI_DEBUG
+ depends on DEBUG_FS
help
Enable in-kernel debugging facilities: statistics, internal
object dump, single step control method execution.
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..102b5e6 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,6 +50,7 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
+acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
new file mode 100644
index 0000000..abc23b2
--- /dev/null
+++ b/drivers/acpi/acpi_dbg.c
@@ -0,0 +1,779 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI : AML: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/acpi_dbg.h>
+#include "internal.h"
+
+#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
+#define ACPI_AML_BUF_SIZE PAGE_SIZE
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define ACPI_AML_OPENED 0x0001
+#define ACPI_AML_CLOSED 0x0002
+#define ACPI_AML_IN_USER 0x0004 /* user space is writing cmd */
+#define ACPI_AML_IN_KERN 0x0008 /* kernel space is reading cmd */
+#define ACPI_AML_OUT_USER 0x0010 /* user space is reading log */
+#define ACPI_AML_OUT_KERN 0x0020 /* kernel space is writing log */
+#define ACPI_AML_USER (ACPI_AML_IN_USER | ACPI_AML_OUT_USER)
+#define ACPI_AML_KERN (ACPI_AML_IN_KERN | ACPI_AML_OUT_KERN)
+#define ACPI_AML_BUSY (ACPI_AML_USER | ACPI_AML_KERN)
+#define ACPI_AML_OPEN (ACPI_AML_OPENED | ACPI_AML_CLOSED)
+
+struct acpi_aml_io {
+ wait_queue_head_t wait;
+ unsigned long flags;
+ unsigned long users;
+ struct mutex lock;
+ struct task_struct *thread;
+ char out_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf out_crc;
+ char in_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf in_crc;
+ acpi_osd_exec_callback function;
+ void *context;
+ unsigned long usages;
+};
+
+static struct acpi_aml_io acpi_aml_io;
+static bool acpi_aml_initialized;
+static struct file *acpi_aml_active_reader;
+static struct dentry *acpi_aml_dentry;
+
+static inline bool __acpi_aml_running(void)
+{
+ return acpi_aml_io.thread ? true : false;
+}
+
+static inline bool __acpi_aml_access_ok(unsigned long flag)
+{
+ /*
+ * The debugger interface is in opened state (OPENED && !CLOSED),
+ * then it is allowed to access the debugger buffers from either
+ * user space or the kernel space.
+ * In addition, for the kernel space, only the debugger thread
+ * (thread ID matched) is allowed to access.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED) ||
+ (acpi_aml_io.flags & ACPI_AML_CLOSED) ||
+ !__acpi_aml_running())
+ return false;
+ if ((flag & ACPI_AML_KERN) &&
+ current != acpi_aml_io.thread)
+ return false;
+ return true;
+}
+
+static inline bool __acpi_aml_readable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another read is not in progress and there is data in buffer
+ * available for read.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_count(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_writable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another write is not in progress and there is buffer space
+ * available for write.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_space(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_busy(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_BUSY)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_opened(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_OPEN)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_used(void)
+{
+ return acpi_aml_io.usages ? true : false;
+}
+
+static inline bool acpi_aml_running(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_running();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_busy(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_busy();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_used(void)
+{
+ bool ret;
+
+ /*
+ * The usage count is prepared to avoid race conditions between the
+ * starts and the stops of the debugger thread.
+ */
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_used();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_KERN) ||
+ __acpi_aml_readable(&acpi_aml_io.in_crc, ACPI_AML_IN_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_KERN) ||
+ __acpi_aml_writable(&acpi_aml_io.out_crc, ACPI_AML_OUT_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_USER) ||
+ __acpi_aml_readable(&acpi_aml_io.out_crc, ACPI_AML_OUT_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_USER) ||
+ __acpi_aml_writable(&acpi_aml_io.in_crc, ACPI_AML_IN_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_write(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_writable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_read(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_readable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static void acpi_aml_unlock_fifo(unsigned long flag, bool wakeup)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~flag;
+ if (wakeup)
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+}
+
+static int acpi_aml_write_kern(const char *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting logs */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ memcpy(p, buf, n);
+ /* sync head after inserting logs */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_KERN, true);
+ return n;
+}
+
+static int acpi_aml_readb_kern(void)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing cmds */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ ret = (int)*p;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ crc->tail = (crc->tail + 1) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_IN_KERN, true);
+ return ret;
+}
+
+/*
+ * acpi_aml_write_log() - Capture debugger output
+ * @msg: the debugger output
+ *
+ * This function should be used to implement acpi_os_printf() to filter out
+ * the debugger output and store the output into the debugger interface
+ * buffer. Return the size of stored logs or errno.
+ */
+ssize_t acpi_aml_write_log(const char *msg)
+{
+ int ret = 0;
+ int count = 0, size = 0;
+
+ if (!acpi_aml_initialized)
+ return -ENODEV;
+ if (msg)
+ count = strlen(msg);
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_kern(msg + size, count);
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ break;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ size += ret;
+ count -= ret;
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_write_log);
+
+/*
+ * acpi_aml_read_cmd() - Capture debugger input
+ * @msg: the debugger input
+ * @size: the size of the debugger input
+ *
+ * This function should be used to implement acpi_os_get_line() to capture
+ * the debugger input commands and store the input commands into the
+ * debugger interface buffer. Return the size of stored commands or errno.
+ */
+ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+{
+ int ret = 0;
+ int size = 0;
+
+ /*
+ * This is ensured by the running fact of the debugger thread
+ * unless a bug is introduced.
+ */
+ BUG_ON(!acpi_aml_initialized);
+ while (count > 0) {
+again:
+ /*
+ * Check each input byte to find the end of the command.
+ */
+ ret = acpi_aml_readb_kern();
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_readable());
+ /*
+ * We need to retry when the condition becomes
+ * true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ *(msg + size) = (char)ret;
+ size++;
+ count--;
+ if (ret == '\n') {
+ /*
+ * acpi_os_get_line() requires a zero terminated command
+ * string.
+ */
+ *(msg + size - 1) = '\0';
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_read_cmd);
+
+static int acpi_aml_thread(void *unsed)
+{
+ acpi_osd_exec_callback function = NULL;
+ void *context;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (acpi_aml_io.function) {
+ acpi_aml_io.usages++;
+ function = acpi_aml_io.function;
+ context = acpi_aml_io.context;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ if (function)
+ function(context);
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.usages--;
+ if (!__acpi_aml_used()) {
+ acpi_aml_io.thread = NULL;
+ wake_up(&acpi_aml_io.wait);
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ return 0;
+}
+
+/*
+ * acpi_aml_create_thread() - Create AML debugger thread
+ * @function: the debugger thread callback
+ * @context: the context to be passed to the debugger thread
+ *
+ * This function should be used to implement acpi_os_execute() which is
+ * used by the ACPICA debugger to create the debugger thread.
+ */
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ struct task_struct *t;
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.function = function;
+ acpi_aml_io.context = context;
+ mutex_unlock(&acpi_aml_io.lock);
+
+ t = kthread_create(acpi_aml_thread, NULL, "aml");
+ if (IS_ERR(t)) {
+ pr_err("Failed to create AML debugger thread.\n");
+ return PTR_ERR(t);
+ }
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.thread = t;
+ acpi_set_debugger_thread_id((acpi_thread_id)(unsigned long)t);
+ wake_up_process(t);
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_create_thread);
+
+int acpi_aml_wait_command_ready(void)
+{
+ acpi_status status;
+
+ if (!acpi_gbl_method_executing)
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+
+ status = acpi_os_get_line(acpi_gbl_db_line_buf,
+ ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_wait_command_ready);
+
+int acpi_aml_notify_command_complete(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_notify_command_complete);
+
+static int acpi_aml_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ acpi_status status;
+
+ mutex_lock(&acpi_aml_io.lock);
+ /*
+ * The debugger interface is being closed, no new user is allowed
+ * during this period.
+ */
+ if (acpi_aml_io.flags & ACPI_AML_CLOSED) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ /*
+ * Only one reader is allowed to initiate the debugger
+ * thread.
+ */
+ if (acpi_aml_active_reader) {
+ ret = -EBUSY;
+ goto err_lock;
+ } else {
+ pr_debug("Opening debugger reader.\n");
+ acpi_aml_active_reader = file;
+ }
+ } else {
+ /*
+ * No writer is allowed unless the debugger thread is
+ * ready.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ }
+ if (acpi_aml_active_reader == file) {
+ pr_debug("Opening debugger interface.\n");
+ mutex_unlock(&acpi_aml_io.lock);
+
+ pr_debug("Initializing debugger thread.\n");
+ status = acpi_initialize_debugger();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Failed to initialize debugger.\n");
+ ret = -EINVAL;
+ goto err_lock;
+ }
+ acpi_aml_io.flags |= ACPI_AML_OPENED;
+ pr_debug("Debugger thread initialized.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0;
+ acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0;
+ pr_debug("Debugger interface opened.\n");
+ }
+ acpi_aml_io.users++;
+err_lock:
+ if (IS_ERR_VALUE(ret)) {
+ if (acpi_aml_active_reader == file)
+ acpi_aml_active_reader = NULL;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.users--;
+ if (file == acpi_aml_active_reader) {
+ pr_debug("Closing debugger reader.\n");
+ acpi_aml_active_reader = NULL;
+
+ pr_debug("Closing debugger interface.\n");
+ acpi_aml_io.flags |= ACPI_AML_CLOSED;
+
+ /*
+ * Wake up all user space/kernel space blocked
+ * readers/writers.
+ */
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+ /*
+ * Wait all user space/kernel space readers/writers to
+ * stop so that ACPICA command loop of the debugger thread
+ * should fail all its command line reads after this point.
+ */
+ wait_event(acpi_aml_io.wait, !acpi_aml_busy());
+
+ /*
+ * Then we try to terminate the debugger thread if it is
+ * not terminated.
+ */
+ pr_debug("Terminating debugger thread.\n");
+ acpi_terminate_debugger();
+ wait_event(acpi_aml_io.wait, !acpi_aml_used());
+ pr_debug("Debugger thread terminated.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~ACPI_AML_OPENED;
+ }
+ if (acpi_aml_io.users == 0) {
+ pr_debug("Debugger interface closed.\n");
+ acpi_aml_io.flags &= ~ACPI_AML_CLOSED;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_read_user(char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing logs */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ n = min(len, circ_count_to_end(crc));
+ ret = copy_to_user(buf, p, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync tail after removing logs */
+ smp_mb();
+ crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !IS_ERR_VALUE(ret));
+ return ret;
+}
+
+static ssize_t acpi_aml_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_read_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_readable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_write_user(const char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ ret = copy_from_user(p, buf, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync head after inserting cmds */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !IS_ERR_VALUE(ret));
+ return n;
+}
+
+static ssize_t acpi_aml_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static unsigned int acpi_aml_poll(struct file *file, poll_table *wait)
+{
+ int masks = 0;
+
+ poll_wait(file, &acpi_aml_io.wait, wait);
+ if (acpi_aml_user_readable())
+ masks |= POLLIN | POLLRDNORM;
+ if (acpi_aml_user_writable())
+ masks |= POLLOUT | POLLWRNORM;
+
+ return masks;
+}
+
+static const struct file_operations acpi_aml_operations = {
+ .read = acpi_aml_read,
+ .write = acpi_aml_write,
+ .poll = acpi_aml_poll,
+ .open = acpi_aml_open,
+ .release = acpi_aml_release,
+ .llseek = generic_file_llseek,
+};
+
+int __init acpi_aml_init(void)
+{
+ if (!acpi_debugfs_dir)
+ return -ENOENT;
+ /* Initialize AML IO interface */
+ mutex_init(&acpi_aml_io.lock);
+ init_waitqueue_head(&acpi_aml_io.wait);
+ acpi_aml_io.out_crc.buf = acpi_aml_io.out_buf;
+ acpi_aml_io.in_crc.buf = acpi_aml_io.in_buf;
+ acpi_aml_dentry = debugfs_create_file("acpidbg",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ acpi_debugfs_dir, NULL,
+ &acpi_aml_operations);
+ if (acpi_aml_dentry == NULL)
+ return -ENODEV;
+ acpi_aml_initialized = true;
+ return 0;
+}
+
+#if 0
+void __exit acpi_aml_exit(void)
+{
+ /* TODO: Stop the in kernel debugger */
+ if (acpi_aml_dentry)
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_initialized = false;
+}
+
+module_init(acpi_aml_init);
+module_exit(acpi_aml_exit);
+#endif
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a212cef..06fbba9 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,6 +37,7 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
+#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1094,6 +1095,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
+ acpi_aml_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 3272915..2de8f66 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,6 +40,7 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
+#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -233,7 +234,8 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- printk(KERN_CONT "%s", buffer);
+ if (acpi_aml_write_log(buffer) < 0)
+ printk(KERN_CONT "%s", buffer);
#endif
}

@@ -1124,6 +1126,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));

+ if (type == OSL_DEBUGGER_MAIN_THREAD) {
+ ret = acpi_aml_create_thread(function, context);
+ if (ret) {
+ pr_err("Call to kthread_create() failed.\n");
+ status = AE_ERROR;
+ }
+ goto out_thread;
+ }
+
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the work_struct list in a
@@ -1148,11 +1159,17 @@ acpi_status acpi_os_execute(acpi_execute_type type,
if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else {
+ } else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ } else {
+ pr_err("Unsupported os_execute type %d.\n", type);
+ status = AE_ERROR;
}

+ if (ACPI_FAILURE(status))
+ goto err_workqueue;
+
/*
* On some machines, a software-initiated SMI causes corruption unless
* the SMI runs on CPU 0. An SMI can be initiated by any AML, but
@@ -1161,13 +1178,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* queueing on CPU 0.
*/
ret = queue_work_on(0, queue, &dpc->work);
-
if (!ret) {
printk(KERN_ERR PREFIX
"Call to queue_work() failed.\n");
status = AE_ERROR;
- kfree(dpc);
}
+err_workqueue:
+ if (ACPI_FAILURE(status))
+ kfree(dpc);
+out_thread:
return status;
}
EXPORT_SYMBOL(acpi_os_execute);
@@ -1355,11 +1374,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
chars = strlen(buffer) - 1;
buffer[chars] = '\0';
}
+#else
+ int ret;
+
+ ret = acpi_aml_read_cmd(buffer, buffer_length);
+ if (ret < 0)
+ return AE_ERROR;
+ if (bytes_read)
+ *bytes_read = ret;
#endif

return AE_OK;
}

+acpi_status acpi_os_wait_command_ready(void)
+{
+ int ret;
+
+ ret = acpi_aml_wait_command_ready();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+acpi_status acpi_os_notify_command_complete(void)
+{
+ int ret;
+
+ ret = acpi_aml_notify_command_complete();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
acpi_status acpi_os_signal(u32 function, void *info)
{
switch (function) {
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 69dbae6..e21857d 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -152,8 +152,6 @@
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index 673fdf4..ceea026 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -138,16 +138,6 @@ static inline void acpi_os_terminate_command_signals(void)
{
}

-static inline acpi_status acpi_os_wait_command_ready(void)
-{
- return AE_ERROR;
-}
-
-static inline acpi_status acpi_os_notify_command_complete(void)
-{
- return AE_ERROR;
-}
-
/*
* OSL interfaces added by Linux
*/
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
new file mode 100644
index 0000000..60f3887
--- /dev/null
+++ b/include/linux/acpi_dbg.h
@@ -0,0 +1,52 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_ACPI_DBG_H
+#define _LINUX_ACPI_DBG_H
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_aml_init(void);
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_aml_write_log(const char *msg);
+ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
+int acpi_aml_wait_command_ready(void);
+int acpi_aml_notify_command_complete(void);
+#else
+static int inline acpi_aml_init(void)
+{
+ return 0;
+}
+static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-11-19 06:08:59

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3 5/6] tools/power/acpi: Add userspace AML interface support

This patch adds a userspace tool to access Linux kernel AML debugger
interface.

Tow modes are supported by this tool:
1. Interactive: Users are able to launch a debugging shell to talk with
in-kernel AML debugger.
Note that it's user duty to ensure kernel runtime integrity by using
this debugging tool:
A. Some control methods evaluated by the users may result in kernel
panics if those control methods shouldn't be evaluated by the OSPMs
according to the current BIOS/OS configurations.
B. Currently if a single stepping evaluation couldn't run to an end,
then the synchronization primitives acquired by the evaluation may
block normal OSPM control method evaluations.
2. Batch: Users are able to execute debugger commands in a script.
Note that in addition to the above duties, it's user duty to ensure
script runtime integrity by using this debugging tool in this mode:
C. Currently only those commands that are not used for single stepping
are suitable to be used in this mode.
D. If the execution of the command may cause a failure that could result
in an endless kernel execution, the execution of the script may also
get blocked.
To exit the utility, currently "exit/quit" commands are recommended, but
ctrl-C" can also be used.

Signed-off-by: Lv Zheng <[email protected]>
---
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 ++
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++++++++++++++++
3 files changed, 473 insertions(+), 8 deletions(-)
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile
index e882c83..a8bf908 100644
--- a/tools/power/acpi/Makefile
+++ b/tools/power/acpi/Makefile
@@ -10,18 +10,18 @@

include ../../scripts/Makefile.include

-all: acpidump ec
-clean: acpidump_clean ec_clean
-install: acpidump_install ec_install
-uninstall: acpidump_uninstall ec_uninstall
+all: acpidbg acpidump ec
+clean: acpidbg_clean acpidump_clean ec_clean
+install: acpidbg_install acpidump_install ec_install
+uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall

-acpidump ec: FORCE
+acpidbg acpidump ec: FORCE
$(call descend,tools/$@,all)
-acpidump_clean ec_clean:
+acpidbg_clean acpidump_clean ec_clean:
$(call descend,tools/$(@:_clean=),clean)
-acpidump_install ec_install:
+acpidbg_install acpidump_install ec_install:
$(call descend,tools/$(@:_install=),install)
-acpidump_uninstall ec_uninstall:
+acpidbg_uninstall acpidump_uninstall ec_uninstall:
$(call descend,tools/$(@:_uninstall=),uninstall)

.PHONY: FORCE
diff --git a/tools/power/acpi/tools/acpidbg/Makefile b/tools/power/acpi/tools/acpidbg/Makefile
new file mode 100644
index 0000000..352df4b
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/Makefile
@@ -0,0 +1,27 @@
+# tools/power/acpi/tools/acpidbg/Makefile - ACPI tool Makefile
+#
+# Copyright (c) 2015, Intel Corporation
+# Author: Lv Zheng <[email protected]>
+#
+# 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.
+
+include ../../Makefile.config
+
+TOOL = acpidbg
+vpath %.c \
+ ../../../../../drivers/acpi/acpica\
+ ../../common\
+ ../../os_specific/service_layers\
+ .
+CFLAGS += -DACPI_APPLICATION -DACPI_SINGLE_THREAD -DACPI_DEBUGGER\
+ -I.\
+ -I../../../../../drivers/acpi/acpica\
+ -I../../../../../include
+LDFLAGS += -lpthread
+TOOL_OBJS = \
+ acpidbg.o
+
+include ../../Makefile.rules
diff --git a/tools/power/acpi/tools/acpidbg/acpidbg.c b/tools/power/acpi/tools/acpidbg/acpidbg.c
new file mode 100644
index 0000000..d070fcc
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/acpidbg.c
@@ -0,0 +1,438 @@
+/*
+ * ACPI AML interfacing userspace utility
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <acpi/acpi.h>
+
+/* Headers not included by include/acpi/platform/aclinux.h */
+#include <stdbool.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/circ_buf.h>
+
+#define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
+#define ACPI_AML_SEC_TICK 1
+#define ACPI_AML_USEC_PEEK 200
+#define ACPI_AML_BUF_SIZE 4096
+
+#define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
+#define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
+#define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
+
+#define ACPI_AML_LOG_START 0x00
+#define ACPI_AML_PROMPT_START 0x01
+#define ACPI_AML_PROMPT_STOP 0x02
+#define ACPI_AML_LOG_STOP 0x03
+#define ACPI_AML_PROMPT_ROLL 0x04
+
+#define ACPI_AML_INTERACTIVE 0x00
+#define ACPI_AML_BATCH 0x01
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
+#define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
+#define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
+#define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
+
+#define ACPI_AML_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) { \
+ fprintf(stderr, \
+ "%s %s pipe closed.\n", #_buf, #_op); \
+ return; \
+ } \
+ } while (0)
+#define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
+ &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) \
+ return; \
+ } while (0)
+
+
+static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
+static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
+static struct circ_buf acpi_aml_cmd_crc = {
+ .buf = acpi_aml_cmd_buf,
+ .head = 0,
+ .tail = 0,
+};
+static struct circ_buf acpi_aml_log_crc = {
+ .buf = acpi_aml_log_buf,
+ .head = 0,
+ .tail = 0,
+};
+static const char *acpi_aml_file_path = ACPI_AML_FILE;
+static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
+static bool acpi_aml_exit;
+
+static bool acpi_aml_batch_drain;
+static unsigned long acpi_aml_batch_state;
+static char acpi_aml_batch_prompt;
+static char acpi_aml_batch_roll;
+static unsigned long acpi_aml_log_state;
+static char *acpi_aml_batch_cmd = NULL;
+static char *acpi_aml_batch_pos = NULL;
+
+static int acpi_aml_set_fl(int fd, int flags)
+{
+ int ret;
+
+ ret = fcntl(fd, F_GETFL, 0);
+ if (ret < 0) {
+ perror("fcntl(F_GETFL)");
+ return ret;
+ }
+ flags |= ret;
+ ret = fcntl(fd, F_SETFL, flags);
+ if (ret < 0) {
+ perror("fcntl(F_SETFL)");
+ return ret;
+ }
+ return ret;
+}
+
+static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
+{
+ if (fd > maxfd)
+ maxfd = fd;
+ FD_SET(fd, set);
+ return maxfd;
+}
+
+static int acpi_aml_read(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ len = read(fd, p, len);
+ if (len < 0)
+ perror("read");
+ else if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int remained = strlen(acpi_aml_batch_pos);
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ if (len > remained) {
+ memcpy(p, acpi_aml_batch_pos, remained);
+ acpi_aml_batch_pos += remained;
+ len = remained;
+ } else {
+ memcpy(p, acpi_aml_batch_pos, len);
+ acpi_aml_batch_pos += len;
+ }
+ if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int ret = 0;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
+ if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
+ *p = acpi_aml_batch_roll;
+ len = 1;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ } else {
+ len = read(fd, p, 1);
+ if (len <= 0) {
+ if (len < 0)
+ perror("read");
+ ret = len;
+ break;
+ }
+ }
+ switch (acpi_aml_log_state) {
+ case ACPI_AML_LOG_START:
+ if (*p == '\n')
+ acpi_aml_log_state = ACPI_AML_PROMPT_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ break;
+ case ACPI_AML_PROMPT_START:
+ if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
+ *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
+ acpi_aml_batch_prompt = *p;
+ acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
+ } else {
+ if (*p != '\n')
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ case ACPI_AML_PROMPT_STOP:
+ if (*p == ' ') {
+ acpi_aml_log_state = ACPI_AML_LOG_STOP;
+ acpi_aml_exit = true;
+ } else {
+ /* Roll back */
+ acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
+ acpi_aml_batch_roll = *p;
+ *p = acpi_aml_batch_prompt;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int acpi_aml_write(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ else if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ if (!acpi_aml_batch_drain) {
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ }
+ if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
+{
+ int len;
+
+ len = acpi_aml_write(fd, crc);
+ if (circ_count_to_end(crc) == 0)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ return len;
+}
+
+static void acpi_aml_loop(int fd)
+{
+ fd_set rfds;
+ fd_set wfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ if (acpi_aml_mode == ACPI_AML_BATCH) {
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ acpi_aml_batch_pos = acpi_aml_batch_cmd;
+ if (acpi_aml_batch_drain)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ else
+ acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
+ }
+ acpi_aml_exit = false;
+ while (!acpi_aml_exit) {
+ tv.tv_sec = ACPI_AML_SEC_TICK;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (acpi_aml_cmd_space()) {
+ if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
+ maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
+ else if (strlen(acpi_aml_batch_pos) &&
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
+ ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
+ }
+ if (acpi_aml_cmd_count() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
+ if (acpi_aml_log_space() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ if (acpi_aml_log_count())
+ maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
+
+ ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ if (ret < 0) {
+ perror("select");
+ break;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(STDIN_FILENO, &rfds))
+ ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
+ if (FD_ISSET(fd, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, write, cmd, ret);
+ else
+ ACPI_AML_DO(fd, write, cmd, ret);
+ }
+ if (FD_ISSET(fd, &rfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, read, log, ret);
+ else
+ ACPI_AML_DO(fd, read, log, ret);
+ }
+ if (FD_ISSET(STDOUT_FILENO, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
+ else
+ ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
+ }
+ }
+ }
+}
+
+static bool acpi_aml_readable(int fd)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = ACPI_AML_USEC_PEEK;
+ FD_ZERO(&rfds);
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
+ if (ret < 0)
+ perror("select");
+ if (ret > 0 && FD_ISSET(fd, &rfds))
+ return true;
+ return false;
+}
+
+/*
+ * This is a userspace IO flush implementation, replying on the prompt
+ * characters and can be turned into a flush() call after kernel implements
+ * .flush() filesystem operation.
+ */
+static void acpi_aml_flush(int fd)
+{
+ while (acpi_aml_readable(fd)) {
+ acpi_aml_batch_drain = true;
+ acpi_aml_loop(fd);
+ acpi_aml_batch_drain = false;
+ }
+}
+
+void usage(FILE *file, char *progname)
+{
+ fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
+ fprintf(file, "\nOptions:\n");
+ fprintf(file, " -b Specify command to be executed in batch mode\n");
+ fprintf(file, " -f Specify interface file other than");
+ fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n");
+ fprintf(file, " -h Print this help message\n");
+}
+
+int main(int argc, char **argv)
+{
+ int fd = 0;
+ int ch;
+ int len;
+ int ret = EXIT_SUCCESS;
+
+ while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
+ switch (ch) {
+ case 'b':
+ if (acpi_aml_batch_cmd) {
+ fprintf(stderr, "Already specify %s\n",
+ acpi_aml_batch_cmd);
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ len = strlen(optarg);
+ acpi_aml_batch_cmd = calloc(len + 2, 1);
+ if (!acpi_aml_batch_cmd) {
+ perror("calloc");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ memcpy(acpi_aml_batch_cmd, optarg, len);
+ acpi_aml_batch_cmd[len] = '\n';
+ acpi_aml_mode = ACPI_AML_BATCH;
+ break;
+ case 'f':
+ acpi_aml_file_path = optarg;
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ goto exit;
+ break;
+ case '?':
+ default:
+ usage(stderr, argv[0]);
+ ret = EXIT_FAILURE;
+ goto exit;
+ break;
+ }
+ }
+
+ fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ perror("open");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
+ acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
+
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ acpi_aml_flush(fd);
+ acpi_aml_loop(fd);
+
+exit:
+ if (fd < 0)
+ close(fd);
+ if (acpi_aml_batch_cmd)
+ free(acpi_aml_batch_cmd);
+ return ret;
+}
--
1.7.10

2015-11-19 06:09:06

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3 6/6] ACPI / debugger: Add module support for ACPI debugger

This patch converts AML debugger into a loadable module.

Note that, it implements driver unloading at the level dependent on the
module reference count. Which means if ACPI debugger is being used by a
userspace program, "rmmod acpi_dbg" should result in failure.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 12 ++-
drivers/acpi/Makefile | 2 +-
drivers/acpi/acpi_dbg.c | 80 ++++++++++++------
drivers/acpi/bus.c | 3 +-
drivers/acpi/osl.c | 207 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/acpi.h | 71 ++++++++++++++++
include/linux/acpi_dbg.h | 52 ------------
7 files changed, 338 insertions(+), 89 deletions(-)
delete mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 4de3517..c4d4a05 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -60,13 +60,23 @@ config ACPI_CCA_REQUIRED
config ACPI_DEBUGGER
bool "In-kernel debugger"
select ACPI_DEBUG
- depends on DEBUG_FS
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.

+if ACPI_DEBUGGER
+
+config ACPI_DEBUGGER_USER
+ tristate "Userspace debugger accessiblity"
+ depends on DEBUG_FS
+ help
+ Export /sys/kernel/debug/acpi/acpidbg for userspace utilities
+ to access the debugger functionalities.
+
+endif
+
config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 102b5e6..c6f236f 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,7 +50,6 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
-acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
@@ -80,6 +79,7 @@ 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
+obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o

# processor has its own "processor." module_param namespace
processor-y := processor_driver.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
index abc23b2..381beb2 100644
--- a/drivers/acpi/acpi_dbg.c
+++ b/drivers/acpi/acpi_dbg.c
@@ -21,7 +21,7 @@
#include <linux/proc_fs.h>
#include <linux/debugfs.h>
#include <linux/circ_buf.h>
-#include <linux/acpi_dbg.h>
+#include <linux/acpi.h>
#include "internal.h"

#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
@@ -307,7 +307,7 @@ static int acpi_aml_readb_kern(void)
* the debugger output and store the output into the debugger interface
* buffer. Return the size of stored logs or errno.
*/
-ssize_t acpi_aml_write_log(const char *msg)
+static ssize_t acpi_aml_write_log(const char *msg)
{
int ret = 0;
int count = 0, size = 0;
@@ -337,7 +337,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_write_log);

/*
* acpi_aml_read_cmd() - Capture debugger input
@@ -348,7 +347,7 @@ EXPORT_SYMBOL(acpi_aml_write_log);
* the debugger input commands and store the input commands into the
* debugger interface buffer. Return the size of stored commands or errno.
*/
-ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+static ssize_t acpi_aml_read_cmd(char *msg, size_t count)
{
int ret = 0;
int size = 0;
@@ -390,7 +389,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_read_cmd);

static int acpi_aml_thread(void *unsed)
{
@@ -427,7 +425,7 @@ static int acpi_aml_thread(void *unsed)
* This function should be used to implement acpi_os_execute() which is
* used by the ACPICA debugger to create the debugger thread.
*/
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+static int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
{
struct task_struct *t;

@@ -449,30 +447,27 @@ int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
mutex_unlock(&acpi_aml_io.lock);
return 0;
}
-EXPORT_SYMBOL(acpi_aml_create_thread);

-int acpi_aml_wait_command_ready(void)
+static int acpi_aml_wait_command_ready(bool single_step,
+ char *buffer, size_t length)
{
acpi_status status;

- if (!acpi_gbl_method_executing)
- acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
- else
+ if (single_step)
acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_get_line(buffer, length, NULL);
if (ACPI_FAILURE(status))
return -EINVAL;
return 0;
}
-EXPORT_SYMBOL(acpi_aml_wait_command_ready);

-int acpi_aml_notify_command_complete(void)
+static int acpi_aml_notify_command_complete(void)
{
return 0;
}
-EXPORT_SYMBOL(acpi_aml_notify_command_complete);

static int acpi_aml_open(struct inode *inode, struct file *file)
{
@@ -746,10 +741,23 @@ static const struct file_operations acpi_aml_operations = {
.llseek = generic_file_llseek,
};

+static const struct acpi_debugger_ops acpi_aml_debugger = {
+ .create_thread = acpi_aml_create_thread,
+ .read_cmd = acpi_aml_read_cmd,
+ .write_log = acpi_aml_write_log,
+ .wait_command_ready = acpi_aml_wait_command_ready,
+ .notify_command_complete = acpi_aml_notify_command_complete,
+};
+
int __init acpi_aml_init(void)
{
- if (!acpi_debugfs_dir)
- return -ENOENT;
+ int ret = 0;
+
+ if (!acpi_debugfs_dir) {
+ ret = -ENOENT;
+ goto err_exit;
+ }
+
/* Initialize AML IO interface */
mutex_init(&acpi_aml_io.lock);
init_waitqueue_head(&acpi_aml_io.wait);
@@ -759,21 +767,39 @@ int __init acpi_aml_init(void)
S_IFREG | S_IRUGO | S_IWUSR,
acpi_debugfs_dir, NULL,
&acpi_aml_operations);
- if (acpi_aml_dentry == NULL)
- return -ENODEV;
+ if (acpi_aml_dentry == NULL) {
+ ret = -ENODEV;
+ goto err_exit;
+ }
+ ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger);
+ if (ret)
+ goto err_fs;
acpi_aml_initialized = true;
- return 0;
+
+err_fs:
+ if (ret) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+err_exit:
+ return ret;
}

-#if 0
void __exit acpi_aml_exit(void)
{
- /* TODO: Stop the in kernel debugger */
- if (acpi_aml_dentry)
- debugfs_remove(acpi_aml_dentry);
- acpi_aml_initialized = false;
+ if (acpi_aml_initialized) {
+ acpi_unregister_debugger(&acpi_aml_debugger);
+ if (acpi_aml_dentry) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+ acpi_aml_initialized = false;
+ }
}

module_init(acpi_aml_init);
module_exit(acpi_aml_exit);
-#endif
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_DESCRIPTION("ACPI debugger userspace IO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 06fbba9..1a40111 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,7 +37,6 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
-#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1095,7 +1094,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
- acpi_aml_init();
+ acpi_debugger_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 2de8f66..ce5ac00 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,7 +40,6 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
-#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -220,6 +219,7 @@ void acpi_os_printf(const char *fmt, ...)
acpi_os_vprintf(fmt, args);
va_end(args);
}
+EXPORT_SYMBOL(acpi_os_printf);

void acpi_os_vprintf(const char *fmt, va_list args)
{
@@ -234,7 +234,7 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- if (acpi_aml_write_log(buffer) < 0)
+ if (acpi_debugger_write_log(buffer) < 0)
printk(KERN_CONT "%s", buffer);
#endif
}
@@ -1100,6 +1100,200 @@ static void acpi_os_execute_deferred(struct work_struct *work)
kfree(dpc);
}

+#ifdef CONFIG_ACPI_DEBUGGER
+static struct acpi_debugger acpi_debugger;
+static bool acpi_debugger_initialized;
+
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_debugger.lock);
+ if (acpi_debugger.ops) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+
+ acpi_debugger.owner = owner;
+ acpi_debugger.ops = ops;
+
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+EXPORT_SYMBOL(acpi_register_debugger);
+
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+ mutex_lock(&acpi_debugger.lock);
+ if (ops == acpi_debugger.ops) {
+ acpi_debugger.ops = NULL;
+ acpi_debugger.owner = NULL;
+ }
+ mutex_unlock(&acpi_debugger.lock);
+}
+EXPORT_SYMBOL(acpi_unregister_debugger);
+
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ int ret;
+ int (*func)(acpi_osd_exec_callback, void *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->create_thread;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(function, context);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_write_log(const char *msg)
+{
+ ssize_t ret;
+ ssize_t (*func)(const char *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->write_log;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(msg);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length)
+{
+ ssize_t ret;
+ ssize_t (*func)(char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->read_cmd;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(buffer, buffer_length);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_wait_command_ready(void)
+{
+ int ret;
+ int (*func)(bool, char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->wait_command_ready;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(acpi_gbl_method_executing,
+ acpi_gbl_db_line_buf, ACPI_DB_LINE_BUFFER_SIZE);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_notify_command_complete(void)
+{
+ int ret;
+ int (*func)(void);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->notify_command_complete;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func();
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int __init acpi_debugger_init(void)
+{
+ mutex_init(&acpi_debugger.lock);
+ acpi_debugger_initialized = true;
+ return 0;
+}
+#endif
+
/*******************************************************************************
*
* FUNCTION: acpi_os_execute
@@ -1127,7 +1321,7 @@ acpi_status acpi_os_execute(acpi_execute_type type,
function, context));

if (type == OSL_DEBUGGER_MAIN_THREAD) {
- ret = acpi_aml_create_thread(function, context);
+ ret = acpi_debugger_create_thread(function, context);
if (ret) {
pr_err("Call to kthread_create() failed.\n");
status = AE_ERROR;
@@ -1377,7 +1571,7 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
#else
int ret;

- ret = acpi_aml_read_cmd(buffer, buffer_length);
+ ret = acpi_debugger_read_cmd(buffer, buffer_length);
if (ret < 0)
return AE_ERROR;
if (bytes_read)
@@ -1386,12 +1580,13 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)

return AE_OK;
}
+EXPORT_SYMBOL(acpi_os_get_line);

acpi_status acpi_os_wait_command_ready(void)
{
int ret;

- ret = acpi_aml_wait_command_ready();
+ ret = acpi_debugger_wait_command_ready();
if (ret < 0)
return AE_ERROR;
return AE_OK;
@@ -1401,7 +1596,7 @@ acpi_status acpi_os_notify_command_complete(void)
{
int ret;

- ret = acpi_aml_notify_command_complete();
+ ret = acpi_debugger_notify_command_complete();
if (ret < 0)
return AE_ERROR;
return AE_OK;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 51a96a8..8f24e03 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -37,6 +37,8 @@
#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/dynamic_debug.h>
+#include <linux/module.h>
+#include <linux/mutex.h>

#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -119,6 +121,75 @@ typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
typedef int (*acpi_tbl_entry_handler)(struct acpi_subtable_header *header,
const unsigned long end);

+/* Debugger support */
+
+struct acpi_debugger_ops {
+ int (*create_thread)(acpi_osd_exec_callback function, void *context);
+ ssize_t (*write_log)(const char *msg);
+ ssize_t (*read_cmd)(char *buffer, size_t length);
+ int (*wait_command_ready)(bool single_step, char *buffer, size_t length);
+ int (*notify_command_complete)(void);
+};
+
+struct acpi_debugger {
+ const struct acpi_debugger_ops *ops;
+ struct module *owner;
+ struct mutex lock;
+};
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_debugger_init(void);
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops);
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops);
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_debugger_write_log(const char *msg);
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length);
+int acpi_debugger_wait_command_ready(void);
+int acpi_debugger_notify_command_complete(void);
+#else
+static inline int acpi_debugger_init(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ return -ENODEV;
+}
+
+static inline void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+}
+
+static inline int acpi_debugger_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size);
#else
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
deleted file mode 100644
index 60f3887..0000000
--- a/include/linux/acpi_dbg.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * ACPI AML interfacing support
- *
- * Copyright (C) 2015, Intel Corporation
- * Authors: Lv Zheng <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _LINUX_ACPI_DBG_H
-#define _LINUX_ACPI_DBG_H
-
-#include <linux/acpi.h>
-
-#ifdef CONFIG_ACPI_DEBUGGER
-int __init acpi_aml_init(void);
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
-ssize_t acpi_aml_write_log(const char *msg);
-ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
-int acpi_aml_wait_command_ready(void);
-int acpi_aml_notify_command_complete(void);
-#else
-static int inline acpi_aml_init(void)
-{
- return 0;
-}
-static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
- void *context)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_write_log(const char *msg)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_wait_command_ready(void)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_notify_command_complete(void)
-{
- return -ENODEV;
-}
-#endif
-
-#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-11-19 06:09:20

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v3] ACPI / x86: introduce acpi_os_readable() support

From: Chen Yu <[email protected]>

This patch implements acpi_os_readable(). The function is used by ACPICA
AML debugger to validate user specified pointers for dumping the memory as
ACPICA descriptor objects.

Signed-off-by: Chen Yu <[email protected]>
Tested-by: Lv Zheng <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/acenv.h | 1 +
arch/x86/kernel/acpi/boot.c | 44 +++++++++++++++++++++++++++++++++++++
drivers/acpi/Kconfig | 3 +++
drivers/acpi/osl.c | 12 ++++++++++
include/acpi/platform/aclinux.h | 1 -
include/acpi/platform/aclinuxex.h | 5 -----
7 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 96d058a..25585ee 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -19,6 +19,7 @@ config X86
def_bool y
select ACPI_LEGACY_TABLES_LOOKUP if ACPI
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
+ select ACPI_MEMORY_ACCESS_CHECK_SUPPORT if ACPI
select ANON_INODES
select ARCH_CLOCKSOURCE_DATA
select ARCH_DISCARD_MEMBLOCK
diff --git a/arch/x86/include/asm/acenv.h b/arch/x86/include/asm/acenv.h
index 1b010a8..a2a2849 100644
--- a/arch/x86/include/asm/acenv.h
+++ b/arch/x86/include/asm/acenv.h
@@ -20,6 +20,7 @@

int __acpi_acquire_global_lock(unsigned int *lock);
int __acpi_release_global_lock(unsigned int *lock);
+bool __acpi_memory_readable(void *pointer, size_t length);

#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \
((Acq) = __acpi_acquire_global_lock(&facs->global_lock))
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index e759076..4a5e22e 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -1699,6 +1699,50 @@ int __acpi_release_global_lock(unsigned int *lock)
return old & 0x1;
}

+bool __acpi_memory_readable(void *pointer, size_t length)
+{
+ unsigned long obj_start, obj_end;
+ unsigned long start_pfn, end_pfn;
+
+ /*
+ * All direct mapped PFNs should have been recorded by the mapped
+ * PFN ranges. But pfn_range_is_mapped() requires the following
+ * sanity checks to be performed before invoking.
+ */
+ obj_start = ACPI_TO_INTEGER(pointer);
+ obj_end = obj_start + length;
+
+ /*
+ * ACPICA core doesn't validate if the object is wrapped over, so
+ * we should.
+ */
+ if (length && (obj_end - 1) < obj_start)
+ return false;
+
+ /*
+ * None direct mapping ranges contain holes. For example, high
+ * kernel map holes (lower than _text or higher than _brk_end).
+ * Converting a virtual address that belongs to the high map hole
+ * results in a valid PFN because its low map has been recorded as
+ * a mapped range.
+ */
+ if (obj_start < PAGE_OFFSET ||
+ obj_start >= (unsigned long)high_memory ||
+ (length && (obj_end - 1) >= (unsigned long)high_memory))
+ return false;
+
+ /*
+ * It is required to pass a range (end_pfn - start_pfn > 0) to
+ * pfn_range_is_mapped().
+ */
+ start_pfn = PFN_DOWN(__pa(obj_start));
+ end_pfn = PFN_UP(__pa(obj_end));
+ if (unlikely(end_pfn == start_pfn))
+ end_pfn++;
+
+ return pfn_range_is_mapped(start_pfn, end_pfn);
+}
+
void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
{
e820_add_region(addr, size, E820_ACPI);
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c4d4a05..73b45f9 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT
config ACPI_CCA_REQUIRED
bool

+config ACPI_MEMORY_ACCESS_CHECK_SUPPORT
+ bool
+
config ACPI_DEBUGGER
bool "In-kernel debugger"
select ACPI_DEBUG
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index ce5ac00..edec035 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -1582,6 +1582,18 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
}
EXPORT_SYMBOL(acpi_os_get_line);

+#ifdef CONFIG_ACPI_MEMORY_ACCESS_CHECK_SUPPORT
+u8 acpi_os_readable(void *pointer, acpi_size length)
+{
+ return __acpi_memory_readable(pointer, length) ? TRUE : FALSE;
+}
+#else
+u8 acpi_os_readable(void *pointer, acpi_size length)
+{
+ return TRUE;
+}
+#endif
+
acpi_status acpi_os_wait_command_ready(void)
{
int ret;
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index e21857d..b3c493e 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -148,7 +148,6 @@
/*
* OSL interfaces used by debugger/disassembler
*/
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index ceea026..94bead6 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -124,11 +124,6 @@ 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;
-}
-
static inline acpi_status acpi_os_initialize_command_signals(void)
{
return AE_OK;
--
1.7.10

2015-12-03 02:40:12

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 0/7] ACPICA / debugger: Add in-kernel AML debugger support

This patchset enables ACPICA debugger for Linux kernel and implements a
userspace utility to access it.

A. Build the AML debugger
In order to build the kernel support of AML debugger, the following kconfig
items should be enabled:
CONFIG_ACPI_DEBUG=y
CONFIG_ACPI_DEBUGGER=y
CONFIG_DEBUG_FS=y
CONFIG_ACPI_DEBUGGER_USER=m
The userspace tool can be found at tools/power/acpi/tools/acpidbg. To
build this utility, staying in tools folder and type "make acpi".

B. Load the AML debugger during runtime
In order to use the in-kernel AML debugger, the following command should be
executed using root user:
# modprobe acpi_dbg
# mount -t debugfs none /sys/kernel/debug
# acpidbg

C. Batch mode
In order to support scripts, the userspace utility also supports single
command batch mode:
# acpidbg -b "help"
# acpidbg -b "tables"
# acpidbg -b "find _LID"
# acpidbg -b "execute \_SB.LID0._LID"
You can find the documentation about the ACPICA debugger commands in:
https://acpica.org/sites/acpica/files/acpica-reference_17.pdf
(The latest document can be found at https://acpica.org/documentation)
And refer to the chapter - ACPICA debugger reference to obtain the full
description of the debugger commands. Note that not all commands are
supported by an in-kernel AML debugger.

D. Unload the AML debugger during runtime
After terminating all acpidbg instances, the following command can be
executed to remove the AML debugger from kernel:
# rmmod acpi_dbg

The following tasks are not completed:
1. .flush() support in the kernel debugger IO driver.
2. multi-commands batch mode.

v2:
1. Fix a help message issue for the userspace acpidbg utility (this is a
typo fix).
2. Fix a batch mode issue by adding "\n" to the prompt sequence (this was
lost due to too many rebase cycles).
3. Fix kconfig dependency for an intermediate patch (this is only useful in
case the latter patches are reverted).
4. Add more accurate runtime debugger stub mechanism and remove old
ACPI_DEBUGGER_EXEC() stub mechanism.
5. Add x86 acpi_os_readable() support.

v3:
1. Remove acpi_os_readable() support from this patchset. It's sent separately.
2. Delete some deprecated comments.

v4:
1. Rebase patch 4/6 because of Kconfig changes.
2. Include acpi_os_readable() back as no objections, also test result is
positive.

Chen Yu (1):
ACPI / x86: introduce acpi_os_readable() support

Colin Ian King (1):
ACPICA: Debugger: Remove unnecessary status check

Lv Zheng (5):
ACPICA: Debugger: Convert some mechanisms to OSPM specific
ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using
different stub mechanism
ACPI / debugger: Add IO interface to access debugger functionalities
tools/power/acpi: Add userspace AML interface support
ACPI / debugger: Add module support for ACPI debugger

arch/x86/Kconfig | 1 +
arch/x86/include/asm/acenv.h | 1 +
arch/x86/kernel/acpi/boot.c | 44 ++
drivers/acpi/Kconfig | 20 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 805 ++++++++++++++++++++++++++++++
drivers/acpi/acpica/acdebug.h | 36 +-
drivers/acpi/acpica/acglobal.h | 5 -
drivers/acpi/acpica/acmacros.h | 11 -
drivers/acpi/acpica/dbdisply.c | 12 +
drivers/acpi/acpica/dbinput.c | 100 +---
drivers/acpi/acpica/dbxface.c | 93 ++--
drivers/acpi/acpica/dscontrol.c | 10 +-
drivers/acpi/acpica/dsutils.c | 16 +-
drivers/acpi/acpica/dswexec.c | 16 +-
drivers/acpi/acpica/utmutex.c | 17 -
drivers/acpi/bus.c | 1 +
drivers/acpi/osl.c | 262 +++++++++-
include/acpi/acpiosxf.h | 18 +-
include/acpi/acpixf.h | 34 ++
include/acpi/platform/aclinux.h | 3 +-
include/acpi/platform/aclinuxex.h | 8 +-
include/linux/acpi.h | 71 +++
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 +
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++
26 files changed, 1852 insertions(+), 214 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

--
1.7.10

2015-12-03 02:43:19

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 1/7] ACPICA: Debugger: Remove unnecessary status check

From: Colin Ian King <[email protected]>

ACPICA commit f9d5c6c9a25e9f5ac05458bfcd8b381e21bb2ba5

ACPICA BZ 1205. Colin Ian King.

Link: https://bugs.acpica.org/show_bug.cgi?id=1205
Link: https://github.com/acpica/acpica/commit/f9d5c6c9
Signed-off-by: Colin Ian King <[email protected]>
Signed-off-by: Bob Moore <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/dbinput.c | 3 ---
1 file changed, 3 deletions(-)

diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index 0480254..fe93f67 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -1246,9 +1246,6 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
* 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,
--
1.7.10

2015-12-03 02:43:03

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 2/7] ACPICA: Debugger: Convert some mechanisms to OSPM specific

The following mechanisms are OSPM specific:
1. Redirect output destination to console: no file redirection will be
needed by an in-kernel debugger, there is even no file can be accessed
when the debugger is running in the kernel mode.
2. Output command prompts: programs other than acpiexec can have different
prompt characters and the prompt characters may be implemented as a
special character sequence to form a char device IO protocol.
3. Command ready/complete handshake: OSPM debugger may wait more conditions
to implement OSPM specific semantics (for example, FIFO full/empty
conditions for O_NONBLOCK or IO open/close conditions).
Leaving such OSPM specific stuffs in the ACPICA debugger core blocks
Linux debugger IO driver implementation.

Several new OSL APIs are provided by this patch:
1. acpi_os_initialize_command_signals: initialize command handshake mechanism
or any other OSPM specific stuffs.
2. acpi_os_terminate_command_signals: reversal of
acpi_os_initialize_command_signals.
3. acpi_os_wait_command_ready: putting debugger task into wait state when a
command is not ready. OSPMs can terminate command loop by returning
AE_CTRL_TERMINATE from this API. Normally, wait_event() or
wait_for_multiple_object() may be used to implement this API.
4. acpi_os_notify_command_complete: putting user task into running state when a
command has been completed. OSPMs can terminate command loop by
returning AE_CTRL_TERMINATE from this API. Normally, wake_up() or
set_event() may be used to implement this API.
This patch also converts current command signaling implementation into a
generic debugger layer (osgendbg.c) to be used by the existing OSPMs or
acpiexec, in return, Linux can have chance to implement its own command
handshake mechanism. This patch also implements acpiexec batch mode in a
multi-threading mode comaptible style as a demo (this can be confirmed by
configuring acpiexec into DEBUGGER_MULTI_THREADED mode where the batch mode
is still working). Lv Zheng.

Note that the OSPM specific command handshake mechanism is required by
Linux kernel because:
1. Linux kernel trends to use wait queue to synchronize two threads, using
mutexes to achieve that will cause false "dead lock" warnings.
2. The command handshake mechanism implemented by ACPICA is implemented in
this way because of a design issue in debugger IO streaming. Debugger IO
outputs are simply cached using a giant buffer, this should be tuned by
Linux in the future.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/acdebug.h | 2 +-
drivers/acpi/acpica/acglobal.h | 5 --
drivers/acpi/acpica/dbinput.c | 97 ++++++++-----------------------------
drivers/acpi/acpica/dbxface.c | 63 +++++++-----------------
drivers/acpi/acpica/utmutex.c | 17 -------
include/acpi/acpiosxf.h | 18 ++++++-
include/acpi/acpixf.h | 11 +++++
include/acpi/platform/aclinux.h | 4 ++
include/acpi/platform/aclinuxex.h | 19 ++++++++
9 files changed, 89 insertions(+), 147 deletions(-)

diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index c928ba4..86474d8 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -257,7 +257,7 @@ acpi_db_command_dispatch(char *input_buffer,

void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context);

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op);
+acpi_status acpi_db_user_commands(void);

char *acpi_db_get_next_token(char *string,
char **next, acpi_object_type * return_type);
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index faa9760..3977134 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -326,7 +326,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list);
#ifdef ACPI_DEBUGGER

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);
@@ -345,7 +344,6 @@ ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]);

/* These buffers should all be the same size */

-ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]);
@@ -360,9 +358,6 @@ 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/dbinput.c b/drivers/acpi/acpica/dbinput.c
index fe93f67..2bf8e6b 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -53,8 +53,6 @@ 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);
@@ -1149,55 +1147,16 @@ acpi_db_command_dispatch(char *input_buffer,

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_db_terminate_loop) {
- 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);
- }
+ (void)acpi_db_user_commands();
acpi_gbl_db_threads_terminated = TRUE;
}

/*******************************************************************************
*
- * 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
+ * PARAMETERS: None
*
* RETURN: None
*
@@ -1206,7 +1165,7 @@ static void acpi_db_single_thread(void)
*
******************************************************************************/

-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
+acpi_status acpi_db_user_commands(void)
{
acpi_status status = AE_OK;

@@ -1216,49 +1175,31 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)

while (!acpi_gbl_db_terminate_loop) {

- /* 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 */
+ /* Wait the readiness of the command */

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_wait_command_ready();
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "While parsing command line"));
- return (status);
+ break;
}

- /* Check for single or multithreaded debug */
+ /* Just call to the command line interpreter */

- 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);
+ acpi_gbl_method_executing = FALSE;
+ acpi_gbl_step_to_next_call = FALSE;

- 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 */
+ (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL,
+ NULL);
+
+ /* Notify the completion of the command */

- acpi_db_single_thread();
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ break;
}
}

+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status, "While parsing command line"));
+ }
return (status);
}
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index 342298a..d95e91f 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -85,46 +85,21 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

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);
+ while (status == AE_CTRL_TRUE) {

- /* Different prompt if method is executing */
+ /* Notify the completion of the command */

- if (!acpi_gbl_method_executing) {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_COMMAND_PROMPT);
- } else {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }

- /* Get the user input line */
+ /* Wait the readiness of the command */

- 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_os_wait_command_ready();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
}

status =
@@ -134,6 +109,11 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,

/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */

+error_exit:
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While parsing/handling command line"));
+ }
return (status);
}

@@ -420,15 +400,7 @@ acpi_status acpi_initialize_debugger(void)

/* 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);
+ status = acpi_os_initialize_command_signals();
if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get debugger mutex\n");
return_ACPI_STATUS(status);
@@ -473,13 +445,14 @@ void acpi_terminate_debugger(void)
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);
}
+
+ acpi_os_terminate_command_signals();
}

if (acpi_gbl_db_buffer) {
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index ce406e3..ea0c207 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -111,17 +111,6 @@ acpi_status acpi_ut_mutex_initialize(void)
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);
}
@@ -162,12 +151,6 @@ 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/acpiosxf.h b/include/acpi/acpiosxf.h
index fbc2baf..0d824a2 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -349,12 +349,28 @@ void acpi_os_redirect_output(void *destination);
#endif

/*
- * Debug input
+ * Debug IO
*/
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line
acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read);
#endif

+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+acpi_status acpi_os_initialize_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+void acpi_os_terminate_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+acpi_status acpi_os_wait_command_ready(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete
+acpi_status acpi_os_notify_command_complete(void);
+#endif
+
/*
* Obtain ACPI table(s)
*/
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 3aaaa86..95ebae3 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -264,6 +264,15 @@ ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0);

/*
+ * Debugger command handshake globals. Host OSes need to access these
+ * variables to implement their own command handshake mechanism.
+ */
+#ifdef ACPI_DEBUGGER
+ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE);
+ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
+#endif
+
+/*
* Other miscellaneous globals
*/
ACPI_GLOBAL(struct acpi_table_fadt, acpi_gbl_FADT);
@@ -929,6 +938,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
void **data,
void (*callback)(void *)))

+void acpi_run_debugger(char *batch_buffer);
+
void acpi_set_debugger_thread_id(acpi_thread_id thread_id);

#endif /* __ACXFACE_H__ */
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 323e5da..69dbae6 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -150,6 +150,10 @@
*/
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index fd6d70f..673fdf4 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -129,6 +129,25 @@ static inline u8 acpi_os_readable(void *pointer, acpi_size length)
return TRUE;
}

+static inline acpi_status acpi_os_initialize_command_signals(void)
+{
+ return AE_OK;
+}
+
+static inline void acpi_os_terminate_command_signals(void)
+{
+}
+
+static inline acpi_status acpi_os_wait_command_ready(void)
+{
+ return AE_ERROR;
+}
+
+static inline acpi_status acpi_os_notify_command_complete(void)
+{
+ return AE_ERROR;
+}
+
/*
* OSL interfaces added by Linux
*/
--
1.7.10

2015-12-03 02:43:12

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 3/7] ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using different stub mechanism

ACPICA commit 11522d6b894054fc4d62dd4f9863ec151296b386

The ACPI_DEBUGGER_EXEC is a problem now when the debugger code is compiled
but runtime disabled. They actually will get executed in this situation.
Although such executions are harmless if we can correctly make
acpi_db_single_step() a runtime stub, users may still do not want to see the
debugger print messages logged into OSPMs' kernel logs when a debugger
driver is not loaded to enable the debugger during runtime.

This patch fixes this issue by introducing new stub mechanism instead of
ACPI_DEBUGGER_EXEC. Lv Zheng.

Link: https://github.com/acpica/acpica/commit/11522d6b
Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpica/acdebug.h | 34 ++++++++++++++++++++++++----------
drivers/acpi/acpica/acmacros.h | 11 -----------
drivers/acpi/acpica/dbdisply.c | 12 ++++++++++++
drivers/acpi/acpica/dbxface.c | 30 ++++++++++++++++++++++++++++++
drivers/acpi/acpica/dscontrol.c | 10 ++--------
drivers/acpi/acpica/dsutils.c | 16 ++++++++--------
drivers/acpi/acpica/dswexec.c | 16 +++++++---------
include/acpi/acpixf.h | 23 +++++++++++++++++++++++
8 files changed, 106 insertions(+), 46 deletions(-)

diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index 86474d8..dcaa15d 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -80,9 +80,15 @@ struct acpi_db_execute_walk {
/*
* dbxface - external debugger interfaces
*/
-acpi_status
-acpi_db_single_step(struct acpi_walk_state *walk_state,
- union acpi_parse_object *op, u32 op_type);
+ACPI_DBR_DEPENDENT_RETURN_OK(acpi_status
+ acpi_db_single_step(struct acpi_walk_state
+ *walk_state,
+ union acpi_parse_object *op,
+ u32 op_type))
+ ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_signal_break_point(struct
+ acpi_walk_state
+ *walk_state))

/*
* dbcmds - debug commands and output routines
@@ -182,11 +188,15 @@ void acpi_db_display_method_info(union acpi_parse_object *op);

void acpi_db_decode_and_display_object(char *target, char *output_type);

-void
-acpi_db_display_result_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_result_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))

-acpi_status acpi_db_display_all_methods(char *display_count_arg);
+ acpi_status acpi_db_display_all_methods(char *display_count_arg);

void acpi_db_display_arguments(void);

@@ -198,9 +208,13 @@ void acpi_db_display_calling_tree(void);

void acpi_db_display_object_type(char *object_arg);

-void
-acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_argument_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))

/*
* dbexec - debugger control method execution
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index e85366c..bad5bca 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -401,17 +401,6 @@
#endif

/*
- * Some code only gets executed when the debugger is built in.
- * Note that this is entirely independent of whether the
- * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not.
- */
-#ifdef ACPI_DEBUGGER
-#define ACPI_DEBUGGER_EXEC(a) a
-#else
-#define ACPI_DEBUGGER_EXEC(a)
-#endif
-
-/*
* Macros used for ACPICA utilities only
*/

diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c
index 672977e..c42ce8a 100644
--- a/drivers/acpi/acpica/dbdisply.c
+++ b/drivers/acpi/acpica/dbdisply.c
@@ -679,6 +679,12 @@ acpi_db_display_result_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{

+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
/* Only display if single stepping */

if (!acpi_gbl_cm_single_step) {
@@ -708,6 +714,12 @@ acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{

+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
if (!acpi_gbl_cm_single_step) {
return;
}
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index d95e91f..d7ff58e 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -119,6 +119,36 @@ error_exit:

/*******************************************************************************
*
+ * FUNCTION: acpi_db_signal_break_point
+ *
+ * PARAMETERS: walk_state - Current walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called for AML_BREAK_POINT_OP
+ *
+ ******************************************************************************/
+
+void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)
+{
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
+ /*
+ * Set the single-step flag. This will cause the debugger (if present)
+ * to break to the console within the AML debugger at the start of the
+ * next AML instruction.
+ */
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_os_printf("**break** Executed AML BreakPoint opcode\n");
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_db_single_step
*
* PARAMETERS: walk_state - Current walk
diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c
index 435fc16..06a6f7f 100644
--- a/drivers/acpi/acpica/dscontrol.c
+++ b/drivers/acpi/acpica/dscontrol.c
@@ -47,6 +47,7 @@
#include "amlcode.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "acdebug.h"

#define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME("dscontrol")
@@ -348,14 +349,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state,

case AML_BREAK_POINT_OP:

- /*
- * Set the single-step flag. This will cause the debugger (if present)
- * to break to the console within the AML debugger at the start of the
- * next AML instruction.
- */
- ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE);
- ACPI_DEBUGGER_EXEC(acpi_os_printf
- ("**break** Executed AML BreakPoint opcode\n"));
+ acpi_db_signal_break_point(walk_state);

/* Call to the OSL in case OS wants a piece of the action */

diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index ebc577b..e4293a8 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -605,8 +605,8 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+
+ acpi_db_display_argument_object(obj_desc, walk_state);
} else {
/* Check for null name case */

@@ -638,10 +638,11 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"Argument previously created, already stacked\n"));

- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (walk_state->
- operands[walk_state->num_operands -
- 1], walk_state));
+ acpi_db_display_argument_object(walk_state->
+ operands[walk_state->
+ num_operands -
+ 1],
+ walk_state);

/*
* Use value that was already previously returned
@@ -685,8 +686,7 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(status);
}

- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+ acpi_db_display_argument_object(obj_desc, walk_state);
}

return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index df54d46..9cc5761 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -178,8 +178,7 @@ cleanup:

/* Break to debugger to display result */

- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (local_obj_desc, walk_state));
+ acpi_db_display_result_object(local_obj_desc, walk_state);

/*
* Delete the predicate result object (we know that
@@ -386,11 +385,10 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)

/* Call debugger for single step support (DEBUG build only) */

- ACPI_DEBUGGER_EXEC(status =
- acpi_db_single_step(walk_state, op, op_class));
- ACPI_DEBUGGER_EXEC(if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);}
- ) ;
+ status = acpi_db_single_step(walk_state, op, op_class);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }

/* Decode the Opcode Class */

@@ -728,8 +726,8 @@ cleanup:

/* Break to debugger to display result */

- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (walk_state->result_obj, walk_state));
+ acpi_db_display_result_object(walk_state->result_obj,
+ walk_state);

/*
* Delete the result op if and only if:
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 95ebae3..5dfab9c 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -375,6 +375,29 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);

#endif /* ACPI_APPLICATION */

+/*
+ * Debugger prototypes
+ *
+ * All interfaces used by debugger will be configured
+ * out of the ACPICA build unless the ACPI_DEBUGGER
+ * flag is defined.
+ */
+#ifdef ACPI_DEBUGGER
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ ACPI_EXTERNAL_RETURN_OK(prototype)
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ ACPI_EXTERNAL_RETURN_VOID(prototype)
+
+#else
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ static ACPI_INLINE prototype {return(AE_OK);}
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ static ACPI_INLINE prototype {return;}
+
+#endif /* ACPI_DEBUGGER */
+
/*****************************************************************************
*
* ACPICA public interface prototypes
--
1.7.10

2015-12-03 02:43:32

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 4/7] ACPI / debugger: Add IO interface to access debugger functionalities

This patch adds /sys/kernel/debug/acpi/acpidbg, which can be used by
userspace programs to access ACPICA debugger functionalities.

Known issue:
1. IO flush support
acpi_os_notify_command_complete() and acpi_os_wait_command_ready() can
be used by acpi_dbg module to implement .flush() filesystem operation.
While this patch doesn't go that far. It then becomes userspace tool's
duty now to flush old commands before executing new batch mode commands.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 3 +-
drivers/acpi/Makefile | 1 +
drivers/acpi/acpi_dbg.c | 779 +++++++++++++++++++++++++++++++++++++
drivers/acpi/bus.c | 2 +
drivers/acpi/osl.c | 55 ++-
include/acpi/platform/aclinux.h | 2 -
include/acpi/platform/aclinuxex.h | 10 -
include/linux/acpi_dbg.h | 52 +++
8 files changed, 887 insertions(+), 17 deletions(-)
create mode 100644 drivers/acpi/acpi_dbg.c
create mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5eef4cb..2a7e6d4 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -58,8 +58,9 @@ config ACPI_CCA_REQUIRED
bool

config ACPI_DEBUGGER
- bool "AML debugger interface (EXPERIMENTAL)"
+ bool "AML debugger interface"
select ACPI_DEBUG
+ depends on DEBUG_FS
help
Enable in-kernel debugging of AML facilities: statistics, internal
object dump, single step control method execution.
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf3..102b5e6 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,6 +50,7 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
+acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
new file mode 100644
index 0000000..abc23b2
--- /dev/null
+++ b/drivers/acpi/acpi_dbg.c
@@ -0,0 +1,779 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI : AML: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/acpi_dbg.h>
+#include "internal.h"
+
+#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
+#define ACPI_AML_BUF_SIZE PAGE_SIZE
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define ACPI_AML_OPENED 0x0001
+#define ACPI_AML_CLOSED 0x0002
+#define ACPI_AML_IN_USER 0x0004 /* user space is writing cmd */
+#define ACPI_AML_IN_KERN 0x0008 /* kernel space is reading cmd */
+#define ACPI_AML_OUT_USER 0x0010 /* user space is reading log */
+#define ACPI_AML_OUT_KERN 0x0020 /* kernel space is writing log */
+#define ACPI_AML_USER (ACPI_AML_IN_USER | ACPI_AML_OUT_USER)
+#define ACPI_AML_KERN (ACPI_AML_IN_KERN | ACPI_AML_OUT_KERN)
+#define ACPI_AML_BUSY (ACPI_AML_USER | ACPI_AML_KERN)
+#define ACPI_AML_OPEN (ACPI_AML_OPENED | ACPI_AML_CLOSED)
+
+struct acpi_aml_io {
+ wait_queue_head_t wait;
+ unsigned long flags;
+ unsigned long users;
+ struct mutex lock;
+ struct task_struct *thread;
+ char out_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf out_crc;
+ char in_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf in_crc;
+ acpi_osd_exec_callback function;
+ void *context;
+ unsigned long usages;
+};
+
+static struct acpi_aml_io acpi_aml_io;
+static bool acpi_aml_initialized;
+static struct file *acpi_aml_active_reader;
+static struct dentry *acpi_aml_dentry;
+
+static inline bool __acpi_aml_running(void)
+{
+ return acpi_aml_io.thread ? true : false;
+}
+
+static inline bool __acpi_aml_access_ok(unsigned long flag)
+{
+ /*
+ * The debugger interface is in opened state (OPENED && !CLOSED),
+ * then it is allowed to access the debugger buffers from either
+ * user space or the kernel space.
+ * In addition, for the kernel space, only the debugger thread
+ * (thread ID matched) is allowed to access.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED) ||
+ (acpi_aml_io.flags & ACPI_AML_CLOSED) ||
+ !__acpi_aml_running())
+ return false;
+ if ((flag & ACPI_AML_KERN) &&
+ current != acpi_aml_io.thread)
+ return false;
+ return true;
+}
+
+static inline bool __acpi_aml_readable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another read is not in progress and there is data in buffer
+ * available for read.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_count(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_writable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another write is not in progress and there is buffer space
+ * available for write.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_space(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_busy(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_BUSY)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_opened(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_OPEN)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_used(void)
+{
+ return acpi_aml_io.usages ? true : false;
+}
+
+static inline bool acpi_aml_running(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_running();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_busy(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_busy();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_used(void)
+{
+ bool ret;
+
+ /*
+ * The usage count is prepared to avoid race conditions between the
+ * starts and the stops of the debugger thread.
+ */
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_used();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_KERN) ||
+ __acpi_aml_readable(&acpi_aml_io.in_crc, ACPI_AML_IN_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_KERN) ||
+ __acpi_aml_writable(&acpi_aml_io.out_crc, ACPI_AML_OUT_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_USER) ||
+ __acpi_aml_readable(&acpi_aml_io.out_crc, ACPI_AML_OUT_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_USER) ||
+ __acpi_aml_writable(&acpi_aml_io.in_crc, ACPI_AML_IN_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_write(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_writable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_read(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_readable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static void acpi_aml_unlock_fifo(unsigned long flag, bool wakeup)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~flag;
+ if (wakeup)
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+}
+
+static int acpi_aml_write_kern(const char *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting logs */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ memcpy(p, buf, n);
+ /* sync head after inserting logs */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_KERN, true);
+ return n;
+}
+
+static int acpi_aml_readb_kern(void)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing cmds */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ ret = (int)*p;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ crc->tail = (crc->tail + 1) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_IN_KERN, true);
+ return ret;
+}
+
+/*
+ * acpi_aml_write_log() - Capture debugger output
+ * @msg: the debugger output
+ *
+ * This function should be used to implement acpi_os_printf() to filter out
+ * the debugger output and store the output into the debugger interface
+ * buffer. Return the size of stored logs or errno.
+ */
+ssize_t acpi_aml_write_log(const char *msg)
+{
+ int ret = 0;
+ int count = 0, size = 0;
+
+ if (!acpi_aml_initialized)
+ return -ENODEV;
+ if (msg)
+ count = strlen(msg);
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_kern(msg + size, count);
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ break;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ size += ret;
+ count -= ret;
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_write_log);
+
+/*
+ * acpi_aml_read_cmd() - Capture debugger input
+ * @msg: the debugger input
+ * @size: the size of the debugger input
+ *
+ * This function should be used to implement acpi_os_get_line() to capture
+ * the debugger input commands and store the input commands into the
+ * debugger interface buffer. Return the size of stored commands or errno.
+ */
+ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+{
+ int ret = 0;
+ int size = 0;
+
+ /*
+ * This is ensured by the running fact of the debugger thread
+ * unless a bug is introduced.
+ */
+ BUG_ON(!acpi_aml_initialized);
+ while (count > 0) {
+again:
+ /*
+ * Check each input byte to find the end of the command.
+ */
+ ret = acpi_aml_readb_kern();
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_readable());
+ /*
+ * We need to retry when the condition becomes
+ * true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ *(msg + size) = (char)ret;
+ size++;
+ count--;
+ if (ret == '\n') {
+ /*
+ * acpi_os_get_line() requires a zero terminated command
+ * string.
+ */
+ *(msg + size - 1) = '\0';
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+EXPORT_SYMBOL(acpi_aml_read_cmd);
+
+static int acpi_aml_thread(void *unsed)
+{
+ acpi_osd_exec_callback function = NULL;
+ void *context;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (acpi_aml_io.function) {
+ acpi_aml_io.usages++;
+ function = acpi_aml_io.function;
+ context = acpi_aml_io.context;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ if (function)
+ function(context);
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.usages--;
+ if (!__acpi_aml_used()) {
+ acpi_aml_io.thread = NULL;
+ wake_up(&acpi_aml_io.wait);
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ return 0;
+}
+
+/*
+ * acpi_aml_create_thread() - Create AML debugger thread
+ * @function: the debugger thread callback
+ * @context: the context to be passed to the debugger thread
+ *
+ * This function should be used to implement acpi_os_execute() which is
+ * used by the ACPICA debugger to create the debugger thread.
+ */
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ struct task_struct *t;
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.function = function;
+ acpi_aml_io.context = context;
+ mutex_unlock(&acpi_aml_io.lock);
+
+ t = kthread_create(acpi_aml_thread, NULL, "aml");
+ if (IS_ERR(t)) {
+ pr_err("Failed to create AML debugger thread.\n");
+ return PTR_ERR(t);
+ }
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.thread = t;
+ acpi_set_debugger_thread_id((acpi_thread_id)(unsigned long)t);
+ wake_up_process(t);
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_create_thread);
+
+int acpi_aml_wait_command_ready(void)
+{
+ acpi_status status;
+
+ if (!acpi_gbl_method_executing)
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+
+ status = acpi_os_get_line(acpi_gbl_db_line_buf,
+ ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_wait_command_ready);
+
+int acpi_aml_notify_command_complete(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(acpi_aml_notify_command_complete);
+
+static int acpi_aml_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ acpi_status status;
+
+ mutex_lock(&acpi_aml_io.lock);
+ /*
+ * The debugger interface is being closed, no new user is allowed
+ * during this period.
+ */
+ if (acpi_aml_io.flags & ACPI_AML_CLOSED) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ /*
+ * Only one reader is allowed to initiate the debugger
+ * thread.
+ */
+ if (acpi_aml_active_reader) {
+ ret = -EBUSY;
+ goto err_lock;
+ } else {
+ pr_debug("Opening debugger reader.\n");
+ acpi_aml_active_reader = file;
+ }
+ } else {
+ /*
+ * No writer is allowed unless the debugger thread is
+ * ready.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ }
+ if (acpi_aml_active_reader == file) {
+ pr_debug("Opening debugger interface.\n");
+ mutex_unlock(&acpi_aml_io.lock);
+
+ pr_debug("Initializing debugger thread.\n");
+ status = acpi_initialize_debugger();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Failed to initialize debugger.\n");
+ ret = -EINVAL;
+ goto err_lock;
+ }
+ acpi_aml_io.flags |= ACPI_AML_OPENED;
+ pr_debug("Debugger thread initialized.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0;
+ acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0;
+ pr_debug("Debugger interface opened.\n");
+ }
+ acpi_aml_io.users++;
+err_lock:
+ if (IS_ERR_VALUE(ret)) {
+ if (acpi_aml_active_reader == file)
+ acpi_aml_active_reader = NULL;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.users--;
+ if (file == acpi_aml_active_reader) {
+ pr_debug("Closing debugger reader.\n");
+ acpi_aml_active_reader = NULL;
+
+ pr_debug("Closing debugger interface.\n");
+ acpi_aml_io.flags |= ACPI_AML_CLOSED;
+
+ /*
+ * Wake up all user space/kernel space blocked
+ * readers/writers.
+ */
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+ /*
+ * Wait all user space/kernel space readers/writers to
+ * stop so that ACPICA command loop of the debugger thread
+ * should fail all its command line reads after this point.
+ */
+ wait_event(acpi_aml_io.wait, !acpi_aml_busy());
+
+ /*
+ * Then we try to terminate the debugger thread if it is
+ * not terminated.
+ */
+ pr_debug("Terminating debugger thread.\n");
+ acpi_terminate_debugger();
+ wait_event(acpi_aml_io.wait, !acpi_aml_used());
+ pr_debug("Debugger thread terminated.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~ACPI_AML_OPENED;
+ }
+ if (acpi_aml_io.users == 0) {
+ pr_debug("Debugger interface closed.\n");
+ acpi_aml_io.flags &= ~ACPI_AML_CLOSED;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_read_user(char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing logs */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ n = min(len, circ_count_to_end(crc));
+ ret = copy_to_user(buf, p, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync tail after removing logs */
+ smp_mb();
+ crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !IS_ERR_VALUE(ret));
+ return ret;
+}
+
+static ssize_t acpi_aml_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_read_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_readable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_write_user(const char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ ret = copy_from_user(p, buf, n);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+ /* sync head after inserting cmds */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !IS_ERR_VALUE(ret));
+ return n;
+}
+
+static ssize_t acpi_aml_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!buf || count < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static unsigned int acpi_aml_poll(struct file *file, poll_table *wait)
+{
+ int masks = 0;
+
+ poll_wait(file, &acpi_aml_io.wait, wait);
+ if (acpi_aml_user_readable())
+ masks |= POLLIN | POLLRDNORM;
+ if (acpi_aml_user_writable())
+ masks |= POLLOUT | POLLWRNORM;
+
+ return masks;
+}
+
+static const struct file_operations acpi_aml_operations = {
+ .read = acpi_aml_read,
+ .write = acpi_aml_write,
+ .poll = acpi_aml_poll,
+ .open = acpi_aml_open,
+ .release = acpi_aml_release,
+ .llseek = generic_file_llseek,
+};
+
+int __init acpi_aml_init(void)
+{
+ if (!acpi_debugfs_dir)
+ return -ENOENT;
+ /* Initialize AML IO interface */
+ mutex_init(&acpi_aml_io.lock);
+ init_waitqueue_head(&acpi_aml_io.wait);
+ acpi_aml_io.out_crc.buf = acpi_aml_io.out_buf;
+ acpi_aml_io.in_crc.buf = acpi_aml_io.in_buf;
+ acpi_aml_dentry = debugfs_create_file("acpidbg",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ acpi_debugfs_dir, NULL,
+ &acpi_aml_operations);
+ if (acpi_aml_dentry == NULL)
+ return -ENODEV;
+ acpi_aml_initialized = true;
+ return 0;
+}
+
+#if 0
+void __exit acpi_aml_exit(void)
+{
+ /* TODO: Stop the in kernel debugger */
+ if (acpi_aml_dentry)
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_initialized = false;
+}
+
+module_init(acpi_aml_init);
+module_exit(acpi_aml_exit);
+#endif
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a212cef..06fbba9 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,6 +37,7 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
+#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1094,6 +1095,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
+ acpi_aml_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 32d684a..4c13398 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,6 +40,7 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
+#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -234,7 +235,8 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- printk(KERN_CONT "%s", buffer);
+ if (acpi_aml_write_log(buffer) < 0)
+ printk(KERN_CONT "%s", buffer);
#endif
}

@@ -1127,6 +1129,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));

+ if (type == OSL_DEBUGGER_MAIN_THREAD) {
+ ret = acpi_aml_create_thread(function, context);
+ if (ret) {
+ pr_err("Call to kthread_create() failed.\n");
+ status = AE_ERROR;
+ }
+ goto out_thread;
+ }
+
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the work_struct list in a
@@ -1151,11 +1162,17 @@ acpi_status acpi_os_execute(acpi_execute_type type,
if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else {
+ } else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ } else {
+ pr_err("Unsupported os_execute type %d.\n", type);
+ status = AE_ERROR;
}

+ if (ACPI_FAILURE(status))
+ goto err_workqueue;
+
/*
* On some machines, a software-initiated SMI causes corruption unless
* the SMI runs on CPU 0. An SMI can be initiated by any AML, but
@@ -1164,13 +1181,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* queueing on CPU 0.
*/
ret = queue_work_on(0, queue, &dpc->work);
-
if (!ret) {
printk(KERN_ERR PREFIX
"Call to queue_work() failed.\n");
status = AE_ERROR;
- kfree(dpc);
}
+err_workqueue:
+ if (ACPI_FAILURE(status))
+ kfree(dpc);
+out_thread:
return status;
}
EXPORT_SYMBOL(acpi_os_execute);
@@ -1358,11 +1377,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
chars = strlen(buffer) - 1;
buffer[chars] = '\0';
}
+#else
+ int ret;
+
+ ret = acpi_aml_read_cmd(buffer, buffer_length);
+ if (ret < 0)
+ return AE_ERROR;
+ if (bytes_read)
+ *bytes_read = ret;
#endif

return AE_OK;
}

+acpi_status acpi_os_wait_command_ready(void)
+{
+ int ret;
+
+ ret = acpi_aml_wait_command_ready();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+acpi_status acpi_os_notify_command_complete(void)
+{
+ int ret;
+
+ ret = acpi_aml_notify_command_complete();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
acpi_status acpi_os_signal(u32 function, void *info)
{
switch (function) {
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 69dbae6..e21857d 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -152,8 +152,6 @@
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete

/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index 673fdf4..ceea026 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -138,16 +138,6 @@ static inline void acpi_os_terminate_command_signals(void)
{
}

-static inline acpi_status acpi_os_wait_command_ready(void)
-{
- return AE_ERROR;
-}
-
-static inline acpi_status acpi_os_notify_command_complete(void)
-{
- return AE_ERROR;
-}
-
/*
* OSL interfaces added by Linux
*/
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
new file mode 100644
index 0000000..60f3887
--- /dev/null
+++ b/include/linux/acpi_dbg.h
@@ -0,0 +1,52 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_ACPI_DBG_H
+#define _LINUX_ACPI_DBG_H
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_aml_init(void);
+int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_aml_write_log(const char *msg);
+ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
+int acpi_aml_wait_command_ready(void);
+int acpi_aml_notify_command_complete(void);
+#else
+static int inline acpi_aml_init(void)
+{
+ return 0;
+}
+static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+static inline int acpi_aml_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-12-03 02:43:38

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 5/7] tools/power/acpi: Add userspace AML interface support

This patch adds a userspace tool to access Linux kernel AML debugger
interface.

Tow modes are supported by this tool:
1. Interactive: Users are able to launch a debugging shell to talk with
in-kernel AML debugger.
Note that it's user duty to ensure kernel runtime integrity by using
this debugging tool:
A. Some control methods evaluated by the users may result in kernel
panics if those control methods shouldn't be evaluated by the OSPMs
according to the current BIOS/OS configurations.
B. Currently if a single stepping evaluation couldn't run to an end,
then the synchronization primitives acquired by the evaluation may
block normal OSPM control method evaluations.
2. Batch: Users are able to execute debugger commands in a script.
Note that in addition to the above duties, it's user duty to ensure
script runtime integrity by using this debugging tool in this mode:
C. Currently only those commands that are not used for single stepping
are suitable to be used in this mode.
D. If the execution of the command may cause a failure that could result
in an endless kernel execution, the execution of the script may also
get blocked.
To exit the utility, currently "exit/quit" commands are recommended, but
ctrl-C" can also be used.

Signed-off-by: Lv Zheng <[email protected]>
---
tools/power/acpi/Makefile | 16 +-
tools/power/acpi/tools/acpidbg/Makefile | 27 ++
tools/power/acpi/tools/acpidbg/acpidbg.c | 438 ++++++++++++++++++++++++++++++
3 files changed, 473 insertions(+), 8 deletions(-)
create mode 100644 tools/power/acpi/tools/acpidbg/Makefile
create mode 100644 tools/power/acpi/tools/acpidbg/acpidbg.c

diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile
index e882c83..a8bf908 100644
--- a/tools/power/acpi/Makefile
+++ b/tools/power/acpi/Makefile
@@ -10,18 +10,18 @@

include ../../scripts/Makefile.include

-all: acpidump ec
-clean: acpidump_clean ec_clean
-install: acpidump_install ec_install
-uninstall: acpidump_uninstall ec_uninstall
+all: acpidbg acpidump ec
+clean: acpidbg_clean acpidump_clean ec_clean
+install: acpidbg_install acpidump_install ec_install
+uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall

-acpidump ec: FORCE
+acpidbg acpidump ec: FORCE
$(call descend,tools/$@,all)
-acpidump_clean ec_clean:
+acpidbg_clean acpidump_clean ec_clean:
$(call descend,tools/$(@:_clean=),clean)
-acpidump_install ec_install:
+acpidbg_install acpidump_install ec_install:
$(call descend,tools/$(@:_install=),install)
-acpidump_uninstall ec_uninstall:
+acpidbg_uninstall acpidump_uninstall ec_uninstall:
$(call descend,tools/$(@:_uninstall=),uninstall)

.PHONY: FORCE
diff --git a/tools/power/acpi/tools/acpidbg/Makefile b/tools/power/acpi/tools/acpidbg/Makefile
new file mode 100644
index 0000000..352df4b
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/Makefile
@@ -0,0 +1,27 @@
+# tools/power/acpi/tools/acpidbg/Makefile - ACPI tool Makefile
+#
+# Copyright (c) 2015, Intel Corporation
+# Author: Lv Zheng <[email protected]>
+#
+# 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.
+
+include ../../Makefile.config
+
+TOOL = acpidbg
+vpath %.c \
+ ../../../../../drivers/acpi/acpica\
+ ../../common\
+ ../../os_specific/service_layers\
+ .
+CFLAGS += -DACPI_APPLICATION -DACPI_SINGLE_THREAD -DACPI_DEBUGGER\
+ -I.\
+ -I../../../../../drivers/acpi/acpica\
+ -I../../../../../include
+LDFLAGS += -lpthread
+TOOL_OBJS = \
+ acpidbg.o
+
+include ../../Makefile.rules
diff --git a/tools/power/acpi/tools/acpidbg/acpidbg.c b/tools/power/acpi/tools/acpidbg/acpidbg.c
new file mode 100644
index 0000000..d070fcc
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/acpidbg.c
@@ -0,0 +1,438 @@
+/*
+ * ACPI AML interfacing userspace utility
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <acpi/acpi.h>
+
+/* Headers not included by include/acpi/platform/aclinux.h */
+#include <stdbool.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/circ_buf.h>
+
+#define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
+#define ACPI_AML_SEC_TICK 1
+#define ACPI_AML_USEC_PEEK 200
+#define ACPI_AML_BUF_SIZE 4096
+
+#define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
+#define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
+#define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
+
+#define ACPI_AML_LOG_START 0x00
+#define ACPI_AML_PROMPT_START 0x01
+#define ACPI_AML_PROMPT_STOP 0x02
+#define ACPI_AML_LOG_STOP 0x03
+#define ACPI_AML_PROMPT_ROLL 0x04
+
+#define ACPI_AML_INTERACTIVE 0x00
+#define ACPI_AML_BATCH 0x01
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
+#define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
+#define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
+#define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
+
+#define ACPI_AML_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) { \
+ fprintf(stderr, \
+ "%s %s pipe closed.\n", #_buf, #_op); \
+ return; \
+ } \
+ } while (0)
+#define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
+ &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) \
+ return; \
+ } while (0)
+
+
+static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
+static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
+static struct circ_buf acpi_aml_cmd_crc = {
+ .buf = acpi_aml_cmd_buf,
+ .head = 0,
+ .tail = 0,
+};
+static struct circ_buf acpi_aml_log_crc = {
+ .buf = acpi_aml_log_buf,
+ .head = 0,
+ .tail = 0,
+};
+static const char *acpi_aml_file_path = ACPI_AML_FILE;
+static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
+static bool acpi_aml_exit;
+
+static bool acpi_aml_batch_drain;
+static unsigned long acpi_aml_batch_state;
+static char acpi_aml_batch_prompt;
+static char acpi_aml_batch_roll;
+static unsigned long acpi_aml_log_state;
+static char *acpi_aml_batch_cmd = NULL;
+static char *acpi_aml_batch_pos = NULL;
+
+static int acpi_aml_set_fl(int fd, int flags)
+{
+ int ret;
+
+ ret = fcntl(fd, F_GETFL, 0);
+ if (ret < 0) {
+ perror("fcntl(F_GETFL)");
+ return ret;
+ }
+ flags |= ret;
+ ret = fcntl(fd, F_SETFL, flags);
+ if (ret < 0) {
+ perror("fcntl(F_SETFL)");
+ return ret;
+ }
+ return ret;
+}
+
+static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
+{
+ if (fd > maxfd)
+ maxfd = fd;
+ FD_SET(fd, set);
+ return maxfd;
+}
+
+static int acpi_aml_read(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ len = read(fd, p, len);
+ if (len < 0)
+ perror("read");
+ else if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int remained = strlen(acpi_aml_batch_pos);
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ if (len > remained) {
+ memcpy(p, acpi_aml_batch_pos, remained);
+ acpi_aml_batch_pos += remained;
+ len = remained;
+ } else {
+ memcpy(p, acpi_aml_batch_pos, len);
+ acpi_aml_batch_pos += len;
+ }
+ if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int ret = 0;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
+ if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
+ *p = acpi_aml_batch_roll;
+ len = 1;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ } else {
+ len = read(fd, p, 1);
+ if (len <= 0) {
+ if (len < 0)
+ perror("read");
+ ret = len;
+ break;
+ }
+ }
+ switch (acpi_aml_log_state) {
+ case ACPI_AML_LOG_START:
+ if (*p == '\n')
+ acpi_aml_log_state = ACPI_AML_PROMPT_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ break;
+ case ACPI_AML_PROMPT_START:
+ if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
+ *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
+ acpi_aml_batch_prompt = *p;
+ acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
+ } else {
+ if (*p != '\n')
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ case ACPI_AML_PROMPT_STOP:
+ if (*p == ' ') {
+ acpi_aml_log_state = ACPI_AML_LOG_STOP;
+ acpi_aml_exit = true;
+ } else {
+ /* Roll back */
+ acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
+ acpi_aml_batch_roll = *p;
+ *p = acpi_aml_batch_prompt;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int acpi_aml_write(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ else if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ if (!acpi_aml_batch_drain) {
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ }
+ if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
+{
+ int len;
+
+ len = acpi_aml_write(fd, crc);
+ if (circ_count_to_end(crc) == 0)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ return len;
+}
+
+static void acpi_aml_loop(int fd)
+{
+ fd_set rfds;
+ fd_set wfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ if (acpi_aml_mode == ACPI_AML_BATCH) {
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ acpi_aml_batch_pos = acpi_aml_batch_cmd;
+ if (acpi_aml_batch_drain)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ else
+ acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
+ }
+ acpi_aml_exit = false;
+ while (!acpi_aml_exit) {
+ tv.tv_sec = ACPI_AML_SEC_TICK;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (acpi_aml_cmd_space()) {
+ if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
+ maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
+ else if (strlen(acpi_aml_batch_pos) &&
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
+ ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
+ }
+ if (acpi_aml_cmd_count() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
+ if (acpi_aml_log_space() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ if (acpi_aml_log_count())
+ maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
+
+ ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ if (ret < 0) {
+ perror("select");
+ break;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(STDIN_FILENO, &rfds))
+ ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
+ if (FD_ISSET(fd, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, write, cmd, ret);
+ else
+ ACPI_AML_DO(fd, write, cmd, ret);
+ }
+ if (FD_ISSET(fd, &rfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, read, log, ret);
+ else
+ ACPI_AML_DO(fd, read, log, ret);
+ }
+ if (FD_ISSET(STDOUT_FILENO, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
+ else
+ ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
+ }
+ }
+ }
+}
+
+static bool acpi_aml_readable(int fd)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = ACPI_AML_USEC_PEEK;
+ FD_ZERO(&rfds);
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
+ if (ret < 0)
+ perror("select");
+ if (ret > 0 && FD_ISSET(fd, &rfds))
+ return true;
+ return false;
+}
+
+/*
+ * This is a userspace IO flush implementation, replying on the prompt
+ * characters and can be turned into a flush() call after kernel implements
+ * .flush() filesystem operation.
+ */
+static void acpi_aml_flush(int fd)
+{
+ while (acpi_aml_readable(fd)) {
+ acpi_aml_batch_drain = true;
+ acpi_aml_loop(fd);
+ acpi_aml_batch_drain = false;
+ }
+}
+
+void usage(FILE *file, char *progname)
+{
+ fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
+ fprintf(file, "\nOptions:\n");
+ fprintf(file, " -b Specify command to be executed in batch mode\n");
+ fprintf(file, " -f Specify interface file other than");
+ fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n");
+ fprintf(file, " -h Print this help message\n");
+}
+
+int main(int argc, char **argv)
+{
+ int fd = 0;
+ int ch;
+ int len;
+ int ret = EXIT_SUCCESS;
+
+ while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
+ switch (ch) {
+ case 'b':
+ if (acpi_aml_batch_cmd) {
+ fprintf(stderr, "Already specify %s\n",
+ acpi_aml_batch_cmd);
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ len = strlen(optarg);
+ acpi_aml_batch_cmd = calloc(len + 2, 1);
+ if (!acpi_aml_batch_cmd) {
+ perror("calloc");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ memcpy(acpi_aml_batch_cmd, optarg, len);
+ acpi_aml_batch_cmd[len] = '\n';
+ acpi_aml_mode = ACPI_AML_BATCH;
+ break;
+ case 'f':
+ acpi_aml_file_path = optarg;
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ goto exit;
+ break;
+ case '?':
+ default:
+ usage(stderr, argv[0]);
+ ret = EXIT_FAILURE;
+ goto exit;
+ break;
+ }
+ }
+
+ fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ perror("open");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
+ acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
+
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ acpi_aml_flush(fd);
+ acpi_aml_loop(fd);
+
+exit:
+ if (fd < 0)
+ close(fd);
+ if (acpi_aml_batch_cmd)
+ free(acpi_aml_batch_cmd);
+ return ret;
+}
--
1.7.10

2015-12-03 02:43:39

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 6/7] ACPI / debugger: Add module support for ACPI debugger

This patch converts AML debugger into a loadable module.

Note that, it implements driver unloading at the level dependent on the
module reference count. Which means if ACPI debugger is being used by a
userspace program, "rmmod acpi_dbg" should result in failure.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/Kconfig | 16 +++-
drivers/acpi/Makefile | 2 +-
drivers/acpi/acpi_dbg.c | 80 ++++++++++++------
drivers/acpi/bus.c | 3 +-
drivers/acpi/osl.c | 207 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/acpi.h | 71 ++++++++++++++++
include/linux/acpi_dbg.h | 52 ------------
7 files changed, 340 insertions(+), 91 deletions(-)
delete mode 100644 include/linux/acpi_dbg.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 2a7e6d4..82b96ee 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -60,13 +60,23 @@ config ACPI_CCA_REQUIRED
config ACPI_DEBUGGER
bool "AML debugger interface"
select ACPI_DEBUG
- depends on DEBUG_FS
help
- Enable in-kernel debugging of AML facilities: statistics, internal
- object dump, single step control method execution.
+ Enable in-kernel debugging of AML 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.

+if ACPI_DEBUGGER
+
+config ACPI_DEBUGGER_USER
+ tristate "Userspace debugger accessiblity"
+ depends on DEBUG_FS
+ help
+ Export /sys/kernel/debug/acpi/acpidbg for userspace utilities
+ to access the debugger functionalities.
+
+endif
+
config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 102b5e6..c6f236f 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,7 +50,6 @@ acpi-y += sysfs.o
acpi-y += property.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
-acpi-$(CONFIG_ACPI_DEBUGGER) += acpi_dbg.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
@@ -80,6 +79,7 @@ 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
+obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o

# processor has its own "processor." module_param namespace
processor-y := processor_driver.o
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
index abc23b2..381beb2 100644
--- a/drivers/acpi/acpi_dbg.c
+++ b/drivers/acpi/acpi_dbg.c
@@ -21,7 +21,7 @@
#include <linux/proc_fs.h>
#include <linux/debugfs.h>
#include <linux/circ_buf.h>
-#include <linux/acpi_dbg.h>
+#include <linux/acpi.h>
#include "internal.h"

#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
@@ -307,7 +307,7 @@ static int acpi_aml_readb_kern(void)
* the debugger output and store the output into the debugger interface
* buffer. Return the size of stored logs or errno.
*/
-ssize_t acpi_aml_write_log(const char *msg)
+static ssize_t acpi_aml_write_log(const char *msg)
{
int ret = 0;
int count = 0, size = 0;
@@ -337,7 +337,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_write_log);

/*
* acpi_aml_read_cmd() - Capture debugger input
@@ -348,7 +347,7 @@ EXPORT_SYMBOL(acpi_aml_write_log);
* the debugger input commands and store the input commands into the
* debugger interface buffer. Return the size of stored commands or errno.
*/
-ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+static ssize_t acpi_aml_read_cmd(char *msg, size_t count)
{
int ret = 0;
int size = 0;
@@ -390,7 +389,6 @@ again:
}
return size > 0 ? size : ret;
}
-EXPORT_SYMBOL(acpi_aml_read_cmd);

static int acpi_aml_thread(void *unsed)
{
@@ -427,7 +425,7 @@ static int acpi_aml_thread(void *unsed)
* This function should be used to implement acpi_os_execute() which is
* used by the ACPICA debugger to create the debugger thread.
*/
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+static int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
{
struct task_struct *t;

@@ -449,30 +447,27 @@ int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
mutex_unlock(&acpi_aml_io.lock);
return 0;
}
-EXPORT_SYMBOL(acpi_aml_create_thread);

-int acpi_aml_wait_command_ready(void)
+static int acpi_aml_wait_command_ready(bool single_step,
+ char *buffer, size_t length)
{
acpi_status status;

- if (!acpi_gbl_method_executing)
- acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
- else
+ if (single_step)
acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);

- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_get_line(buffer, length, NULL);
if (ACPI_FAILURE(status))
return -EINVAL;
return 0;
}
-EXPORT_SYMBOL(acpi_aml_wait_command_ready);

-int acpi_aml_notify_command_complete(void)
+static int acpi_aml_notify_command_complete(void)
{
return 0;
}
-EXPORT_SYMBOL(acpi_aml_notify_command_complete);

static int acpi_aml_open(struct inode *inode, struct file *file)
{
@@ -746,10 +741,23 @@ static const struct file_operations acpi_aml_operations = {
.llseek = generic_file_llseek,
};

+static const struct acpi_debugger_ops acpi_aml_debugger = {
+ .create_thread = acpi_aml_create_thread,
+ .read_cmd = acpi_aml_read_cmd,
+ .write_log = acpi_aml_write_log,
+ .wait_command_ready = acpi_aml_wait_command_ready,
+ .notify_command_complete = acpi_aml_notify_command_complete,
+};
+
int __init acpi_aml_init(void)
{
- if (!acpi_debugfs_dir)
- return -ENOENT;
+ int ret = 0;
+
+ if (!acpi_debugfs_dir) {
+ ret = -ENOENT;
+ goto err_exit;
+ }
+
/* Initialize AML IO interface */
mutex_init(&acpi_aml_io.lock);
init_waitqueue_head(&acpi_aml_io.wait);
@@ -759,21 +767,39 @@ int __init acpi_aml_init(void)
S_IFREG | S_IRUGO | S_IWUSR,
acpi_debugfs_dir, NULL,
&acpi_aml_operations);
- if (acpi_aml_dentry == NULL)
- return -ENODEV;
+ if (acpi_aml_dentry == NULL) {
+ ret = -ENODEV;
+ goto err_exit;
+ }
+ ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger);
+ if (ret)
+ goto err_fs;
acpi_aml_initialized = true;
- return 0;
+
+err_fs:
+ if (ret) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+err_exit:
+ return ret;
}

-#if 0
void __exit acpi_aml_exit(void)
{
- /* TODO: Stop the in kernel debugger */
- if (acpi_aml_dentry)
- debugfs_remove(acpi_aml_dentry);
- acpi_aml_initialized = false;
+ if (acpi_aml_initialized) {
+ acpi_unregister_debugger(&acpi_aml_debugger);
+ if (acpi_aml_dentry) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+ acpi_aml_initialized = false;
+ }
}

module_init(acpi_aml_init);
module_exit(acpi_aml_exit);
-#endif
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_DESCRIPTION("ACPI debugger userspace IO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 06fbba9..1a40111 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -37,7 +37,6 @@
#include <acpi/apei.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
-#include <linux/acpi_dbg.h>

#include "internal.h"

@@ -1095,7 +1094,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
- acpi_aml_init();
+ acpi_debugger_init();
return 0;
}

diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 4c13398..bb66093 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,7 +40,6 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
-#include <linux/acpi_dbg.h>

#include <asm/io.h>
#include <asm/uaccess.h>
@@ -221,6 +220,7 @@ void acpi_os_printf(const char *fmt, ...)
acpi_os_vprintf(fmt, args);
va_end(args);
}
+EXPORT_SYMBOL(acpi_os_printf);

void acpi_os_vprintf(const char *fmt, va_list args)
{
@@ -235,7 +235,7 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- if (acpi_aml_write_log(buffer) < 0)
+ if (acpi_debugger_write_log(buffer) < 0)
printk(KERN_CONT "%s", buffer);
#endif
}
@@ -1103,6 +1103,200 @@ static void acpi_os_execute_deferred(struct work_struct *work)
kfree(dpc);
}

+#ifdef CONFIG_ACPI_DEBUGGER
+static struct acpi_debugger acpi_debugger;
+static bool acpi_debugger_initialized;
+
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_debugger.lock);
+ if (acpi_debugger.ops) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+
+ acpi_debugger.owner = owner;
+ acpi_debugger.ops = ops;
+
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+EXPORT_SYMBOL(acpi_register_debugger);
+
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+ mutex_lock(&acpi_debugger.lock);
+ if (ops == acpi_debugger.ops) {
+ acpi_debugger.ops = NULL;
+ acpi_debugger.owner = NULL;
+ }
+ mutex_unlock(&acpi_debugger.lock);
+}
+EXPORT_SYMBOL(acpi_unregister_debugger);
+
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ int ret;
+ int (*func)(acpi_osd_exec_callback, void *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->create_thread;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(function, context);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_write_log(const char *msg)
+{
+ ssize_t ret;
+ ssize_t (*func)(const char *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->write_log;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(msg);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length)
+{
+ ssize_t ret;
+ ssize_t (*func)(char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->read_cmd;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(buffer, buffer_length);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_wait_command_ready(void)
+{
+ int ret;
+ int (*func)(bool, char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->wait_command_ready;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(acpi_gbl_method_executing,
+ acpi_gbl_db_line_buf, ACPI_DB_LINE_BUFFER_SIZE);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_notify_command_complete(void)
+{
+ int ret;
+ int (*func)(void);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->notify_command_complete;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func();
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int __init acpi_debugger_init(void)
+{
+ mutex_init(&acpi_debugger.lock);
+ acpi_debugger_initialized = true;
+ return 0;
+}
+#endif
+
/*******************************************************************************
*
* FUNCTION: acpi_os_execute
@@ -1130,7 +1324,7 @@ acpi_status acpi_os_execute(acpi_execute_type type,
function, context));

if (type == OSL_DEBUGGER_MAIN_THREAD) {
- ret = acpi_aml_create_thread(function, context);
+ ret = acpi_debugger_create_thread(function, context);
if (ret) {
pr_err("Call to kthread_create() failed.\n");
status = AE_ERROR;
@@ -1380,7 +1574,7 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
#else
int ret;

- ret = acpi_aml_read_cmd(buffer, buffer_length);
+ ret = acpi_debugger_read_cmd(buffer, buffer_length);
if (ret < 0)
return AE_ERROR;
if (bytes_read)
@@ -1389,12 +1583,13 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)

return AE_OK;
}
+EXPORT_SYMBOL(acpi_os_get_line);

acpi_status acpi_os_wait_command_ready(void)
{
int ret;

- ret = acpi_aml_wait_command_ready();
+ ret = acpi_debugger_wait_command_ready();
if (ret < 0)
return AE_ERROR;
return AE_OK;
@@ -1404,7 +1599,7 @@ acpi_status acpi_os_notify_command_complete(void)
{
int ret;

- ret = acpi_aml_notify_command_complete();
+ ret = acpi_debugger_notify_command_complete();
if (ret < 0)
return AE_ERROR;
return AE_OK;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 1991aea..a03a054 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -37,6 +37,8 @@
#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/dynamic_debug.h>
+#include <linux/module.h>
+#include <linux/mutex.h>

#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -119,6 +121,75 @@ typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
typedef int (*acpi_tbl_entry_handler)(struct acpi_subtable_header *header,
const unsigned long end);

+/* Debugger support */
+
+struct acpi_debugger_ops {
+ int (*create_thread)(acpi_osd_exec_callback function, void *context);
+ ssize_t (*write_log)(const char *msg);
+ ssize_t (*read_cmd)(char *buffer, size_t length);
+ int (*wait_command_ready)(bool single_step, char *buffer, size_t length);
+ int (*notify_command_complete)(void);
+};
+
+struct acpi_debugger {
+ const struct acpi_debugger_ops *ops;
+ struct module *owner;
+ struct mutex lock;
+};
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_debugger_init(void);
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops);
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops);
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_debugger_write_log(const char *msg);
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length);
+int acpi_debugger_wait_command_ready(void);
+int acpi_debugger_notify_command_complete(void);
+#else
+static inline int acpi_debugger_init(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ return -ENODEV;
+}
+
+static inline void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+}
+
+static inline int acpi_debugger_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size);
#else
diff --git a/include/linux/acpi_dbg.h b/include/linux/acpi_dbg.h
deleted file mode 100644
index 60f3887..0000000
--- a/include/linux/acpi_dbg.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * ACPI AML interfacing support
- *
- * Copyright (C) 2015, Intel Corporation
- * Authors: Lv Zheng <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _LINUX_ACPI_DBG_H
-#define _LINUX_ACPI_DBG_H
-
-#include <linux/acpi.h>
-
-#ifdef CONFIG_ACPI_DEBUGGER
-int __init acpi_aml_init(void);
-int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context);
-ssize_t acpi_aml_write_log(const char *msg);
-ssize_t acpi_aml_read_cmd(char *buffer, size_t buffer_length);
-int acpi_aml_wait_command_ready(void);
-int acpi_aml_notify_command_complete(void);
-#else
-static int inline acpi_aml_init(void)
-{
- return 0;
-}
-static inline int acpi_aml_create_thread(acpi_osd_exec_callback function,
- void *context)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_write_log(const char *msg)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_read_cmd(char *buffer, u32 buffer_length)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_wait_command_ready(void)
-{
- return -ENODEV;
-}
-static inline int acpi_aml_notify_command_complete(void)
-{
- return -ENODEV;
-}
-#endif
-
-#endif /* _LINUX_ACPI_DBG_H */
--
1.7.10

2015-12-03 02:44:56

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

From: Chen Yu <[email protected]>

This patch implements acpi_os_readable(). The function is used by ACPICA
AML debugger to validate user specified pointers for dumping the memory as
ACPICA descriptor objects.

Signed-off-by: Chen Yu <[email protected]>
Tested-by: Lv Zheng <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/acenv.h | 1 +
arch/x86/kernel/acpi/boot.c | 44 +++++++++++++++++++++++++++++++++++++
drivers/acpi/Kconfig | 3 +++
drivers/acpi/osl.c | 12 ++++++++++
include/acpi/platform/aclinux.h | 1 -
include/acpi/platform/aclinuxex.h | 5 -----
7 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index db3622f..6593b6a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -19,6 +19,7 @@ config X86
def_bool y
select ACPI_LEGACY_TABLES_LOOKUP if ACPI
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
+ select ACPI_MEMORY_ACCESS_CHECK_SUPPORT if ACPI
select ANON_INODES
select ARCH_CLOCKSOURCE_DATA
select ARCH_DISCARD_MEMBLOCK
diff --git a/arch/x86/include/asm/acenv.h b/arch/x86/include/asm/acenv.h
index 1b010a8..a2a2849 100644
--- a/arch/x86/include/asm/acenv.h
+++ b/arch/x86/include/asm/acenv.h
@@ -20,6 +20,7 @@

int __acpi_acquire_global_lock(unsigned int *lock);
int __acpi_release_global_lock(unsigned int *lock);
+bool __acpi_memory_readable(void *pointer, size_t length);

#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \
((Acq) = __acpi_acquire_global_lock(&facs->global_lock))
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index e759076..4a5e22e 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -1699,6 +1699,50 @@ int __acpi_release_global_lock(unsigned int *lock)
return old & 0x1;
}

+bool __acpi_memory_readable(void *pointer, size_t length)
+{
+ unsigned long obj_start, obj_end;
+ unsigned long start_pfn, end_pfn;
+
+ /*
+ * All direct mapped PFNs should have been recorded by the mapped
+ * PFN ranges. But pfn_range_is_mapped() requires the following
+ * sanity checks to be performed before invoking.
+ */
+ obj_start = ACPI_TO_INTEGER(pointer);
+ obj_end = obj_start + length;
+
+ /*
+ * ACPICA core doesn't validate if the object is wrapped over, so
+ * we should.
+ */
+ if (length && (obj_end - 1) < obj_start)
+ return false;
+
+ /*
+ * None direct mapping ranges contain holes. For example, high
+ * kernel map holes (lower than _text or higher than _brk_end).
+ * Converting a virtual address that belongs to the high map hole
+ * results in a valid PFN because its low map has been recorded as
+ * a mapped range.
+ */
+ if (obj_start < PAGE_OFFSET ||
+ obj_start >= (unsigned long)high_memory ||
+ (length && (obj_end - 1) >= (unsigned long)high_memory))
+ return false;
+
+ /*
+ * It is required to pass a range (end_pfn - start_pfn > 0) to
+ * pfn_range_is_mapped().
+ */
+ start_pfn = PFN_DOWN(__pa(obj_start));
+ end_pfn = PFN_UP(__pa(obj_end));
+ if (unlikely(end_pfn == start_pfn))
+ end_pfn++;
+
+ return pfn_range_is_mapped(start_pfn, end_pfn);
+}
+
void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
{
e820_add_region(addr, size, E820_ACPI);
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 82b96ee..9422133 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT
config ACPI_CCA_REQUIRED
bool

+config ACPI_MEMORY_ACCESS_CHECK_SUPPORT
+ bool
+
config ACPI_DEBUGGER
bool "AML debugger interface"
select ACPI_DEBUG
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index bb66093..14a3beb 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -1585,6 +1585,18 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
}
EXPORT_SYMBOL(acpi_os_get_line);

+#ifdef CONFIG_ACPI_MEMORY_ACCESS_CHECK_SUPPORT
+u8 acpi_os_readable(void *pointer, acpi_size length)
+{
+ return __acpi_memory_readable(pointer, length) ? TRUE : FALSE;
+}
+#else
+u8 acpi_os_readable(void *pointer, acpi_size length)
+{
+ return TRUE;
+}
+#endif
+
acpi_status acpi_os_wait_command_ready(void)
{
int ret;
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index e21857d..b3c493e 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -148,7 +148,6 @@
/*
* OSL interfaces used by debugger/disassembler
*/
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index ceea026..94bead6 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -124,11 +124,6 @@ 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;
-}
-
static inline acpi_status acpi_os_initialize_command_signals(void)
{
return AE_OK;
--
1.7.10

2015-12-03 22:28:04

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v4 4/7] ACPI / debugger: Add IO interface to access debugger functionalities

On Wed, Dec 2, 2015 at 6:43 PM, Lv Zheng <[email protected]> wrote:
> This patch adds /sys/kernel/debug/acpi/acpidbg, which can be used by
> userspace programs to access ACPICA debugger functionalities.

What's this one generated against? My Kconfig contains:

config ACPI_DEBUGGER
bool "In-kernel debugger (EXPERIMENTAL)"
select ACPI_DEBUG

which isn't quite the same as what you generated this against. I'm
starting with 4.4-rc3.

--Andy

2015-12-03 23:04:20

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v4 4/7] ACPI / debugger: Add IO interface to access debugger functionalities

On Thursday, December 03, 2015 02:27:41 PM Andy Lutomirski wrote:
> On Wed, Dec 2, 2015 at 6:43 PM, Lv Zheng <[email protected]> wrote:
> > This patch adds /sys/kernel/debug/acpi/acpidbg, which can be used by
> > userspace programs to access ACPICA debugger functionalities.
>
> What's this one generated against? My Kconfig contains:
>
> config ACPI_DEBUGGER
> bool "In-kernel debugger (EXPERIMENTAL)"
> select ACPI_DEBUG
>
> which isn't quite the same as what you generated this against. I'm
> starting with 4.4-rc3.

Probably on top of linux-next (or my linux-next branch).

There's a fix commit against Kconfig in my tree which is going to be pushed
for 4.4-rc4 tomorrow.

Thanks,
Rafael

2015-12-14 23:28:41

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

On Wed, Dec 2, 2015 at 6:43 PM, Lv Zheng <[email protected]> wrote:
> From: Chen Yu <[email protected]>
>
> This patch implements acpi_os_readable(). The function is used by ACPICA
> AML debugger to validate user specified pointers for dumping the memory as
> ACPICA descriptor objects.
>
> Signed-off-by: Chen Yu <[email protected]>
> Tested-by: Lv Zheng <[email protected]>
> Signed-off-by: Lv Zheng <[email protected]>
> ---
> arch/x86/Kconfig | 1 +
> arch/x86/include/asm/acenv.h | 1 +
> arch/x86/kernel/acpi/boot.c | 44 +++++++++++++++++++++++++++++++++++++
> drivers/acpi/Kconfig | 3 +++
> drivers/acpi/osl.c | 12 ++++++++++
> include/acpi/platform/aclinux.h | 1 -
> include/acpi/platform/aclinuxex.h | 5 -----
> 7 files changed, 61 insertions(+), 6 deletions(-)
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index db3622f..6593b6a 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -19,6 +19,7 @@ config X86
> def_bool y
> select ACPI_LEGACY_TABLES_LOOKUP if ACPI
> select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
> + select ACPI_MEMORY_ACCESS_CHECK_SUPPORT if ACPI
> select ANON_INODES
> select ARCH_CLOCKSOURCE_DATA
> select ARCH_DISCARD_MEMBLOCK
> diff --git a/arch/x86/include/asm/acenv.h b/arch/x86/include/asm/acenv.h
> index 1b010a8..a2a2849 100644
> --- a/arch/x86/include/asm/acenv.h
> +++ b/arch/x86/include/asm/acenv.h
> @@ -20,6 +20,7 @@
>
> int __acpi_acquire_global_lock(unsigned int *lock);
> int __acpi_release_global_lock(unsigned int *lock);
> +bool __acpi_memory_readable(void *pointer, size_t length);
>
> #define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \
> ((Acq) = __acpi_acquire_global_lock(&facs->global_lock))
> diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
> index e759076..4a5e22e 100644
> --- a/arch/x86/kernel/acpi/boot.c
> +++ b/arch/x86/kernel/acpi/boot.c
> @@ -1699,6 +1699,50 @@ int __acpi_release_global_lock(unsigned int *lock)
> return old & 0x1;
> }
>
> +bool __acpi_memory_readable(void *pointer, size_t length)
> +{
> + unsigned long obj_start, obj_end;
> + unsigned long start_pfn, end_pfn;

What does "readable" mean in this context?

--Andy

2015-12-14 23:13:33

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

On Thursday, December 03, 2015 10:43:21 AM Lv Zheng wrote:
> From: Chen Yu <[email protected]>
>
> This patch implements acpi_os_readable(). The function is used by ACPICA
> AML debugger to validate user specified pointers for dumping the memory as
> ACPICA descriptor objects.
>
> Signed-off-by: Chen Yu <[email protected]>
> Tested-by: Lv Zheng <[email protected]>
> Signed-off-by: Lv Zheng <[email protected]>

This has to be resent with CCs to the x86 maintainers.

Thanks,
Rafael

2015-12-14 23:14:34

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v4 0/7] ACPICA / debugger: Add in-kernel AML debugger support

On Thursday, December 03, 2015 10:40:00 AM Lv Zheng wrote:
> This patchset enables ACPICA debugger for Linux kernel and implements a
> userspace utility to access it.
>
> A. Build the AML debugger
> In order to build the kernel support of AML debugger, the following kconfig
> items should be enabled:
> CONFIG_ACPI_DEBUG=y
> CONFIG_ACPI_DEBUGGER=y
> CONFIG_DEBUG_FS=y
> CONFIG_ACPI_DEBUGGER_USER=m
> The userspace tool can be found at tools/power/acpi/tools/acpidbg. To
> build this utility, staying in tools folder and type "make acpi".
>
> B. Load the AML debugger during runtime
> In order to use the in-kernel AML debugger, the following command should be
> executed using root user:
> # modprobe acpi_dbg
> # mount -t debugfs none /sys/kernel/debug
> # acpidbg
>
> C. Batch mode
> In order to support scripts, the userspace utility also supports single
> command batch mode:
> # acpidbg -b "help"
> # acpidbg -b "tables"
> # acpidbg -b "find _LID"
> # acpidbg -b "execute \_SB.LID0._LID"
> You can find the documentation about the ACPICA debugger commands in:
> https://acpica.org/sites/acpica/files/acpica-reference_17.pdf
> (The latest document can be found at https://acpica.org/documentation)
> And refer to the chapter - ACPICA debugger reference to obtain the full
> description of the debugger commands. Note that not all commands are
> supported by an in-kernel AML debugger.
>
> D. Unload the AML debugger during runtime
> After terminating all acpidbg instances, the following command can be
> executed to remove the AML debugger from kernel:
> # rmmod acpi_dbg
>
> The following tasks are not completed:
> 1. .flush() support in the kernel debugger IO driver.
> 2. multi-commands batch mode.
>
> v2:
> 1. Fix a help message issue for the userspace acpidbg utility (this is a
> typo fix).
> 2. Fix a batch mode issue by adding "\n" to the prompt sequence (this was
> lost due to too many rebase cycles).
> 3. Fix kconfig dependency for an intermediate patch (this is only useful in
> case the latter patches are reverted).
> 4. Add more accurate runtime debugger stub mechanism and remove old
> ACPI_DEBUGGER_EXEC() stub mechanism.
> 5. Add x86 acpi_os_readable() support.
>
> v3:
> 1. Remove acpi_os_readable() support from this patchset. It's sent separately.
> 2. Delete some deprecated comments.
>
> v4:
> 1. Rebase patch 4/6 because of Kconfig changes.
> 2. Include acpi_os_readable() back as no objections, also test result is
> positive.
>
> Chen Yu (1):
> ACPI / x86: introduce acpi_os_readable() support
>
> Colin Ian King (1):
> ACPICA: Debugger: Remove unnecessary status check
>
> Lv Zheng (5):
> ACPICA: Debugger: Convert some mechanisms to OSPM specific
> ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using
> different stub mechanism
> ACPI / debugger: Add IO interface to access debugger functionalities
> tools/power/acpi: Add userspace AML interface support
> ACPI / debugger: Add module support for ACPI debugger

OK, I'm queuing up [1-6/7] for 4.5.

The [7/7] needs at least to be looked at by the x86 maintainers.

Thanks,
Rafael

2015-12-15 02:55:39

by Zheng, Lv

[permalink] [raw]
Subject: RE: [PATCH v4 0/7] ACPICA / debugger: Add in-kernel AML debugger support

Hi, Rafael

> From: Rafael J. Wysocki [mailto:[email protected]]
> Sent: Tuesday, December 15, 2015 7:45 AM
>
> On Thursday, December 03, 2015 10:40:00 AM Lv Zheng wrote:
> > This patchset enables ACPICA debugger for Linux kernel and implements a
> > userspace utility to access it.
> >
> > A. Build the AML debugger
> > In order to build the kernel support of AML debugger, the following kconfig
> > items should be enabled:
> > CONFIG_ACPI_DEBUG=y
> > CONFIG_ACPI_DEBUGGER=y
> > CONFIG_DEBUG_FS=y
> > CONFIG_ACPI_DEBUGGER_USER=m
> > The userspace tool can be found at tools/power/acpi/tools/acpidbg. To
> > build this utility, staying in tools folder and type "make acpi".
> >
> > B. Load the AML debugger during runtime
> > In order to use the in-kernel AML debugger, the following command should
> be
> > executed using root user:
> > # modprobe acpi_dbg
> > # mount -t debugfs none /sys/kernel/debug
> > # acpidbg
> >
> > C. Batch mode
> > In order to support scripts, the userspace utility also supports single
> > command batch mode:
> > # acpidbg -b "help"
> > # acpidbg -b "tables"
> > # acpidbg -b "find _LID"
> > # acpidbg -b "execute \_SB.LID0._LID"
> > You can find the documentation about the ACPICA debugger commands in:
> > https://acpica.org/sites/acpica/files/acpica-reference_17.pdf
> > (The latest document can be found at https://acpica.org/documentation)
> > And refer to the chapter - ACPICA debugger reference to obtain the full
> > description of the debugger commands. Note that not all commands are
> > supported by an in-kernel AML debugger.
> >
> > D. Unload the AML debugger during runtime
> > After terminating all acpidbg instances, the following command can be
> > executed to remove the AML debugger from kernel:
> > # rmmod acpi_dbg
> >
> > The following tasks are not completed:
> > 1. .flush() support in the kernel debugger IO driver.
> > 2. multi-commands batch mode.
> >
> > v2:
> > 1. Fix a help message issue for the userspace acpidbg utility (this is a
> > typo fix).
> > 2. Fix a batch mode issue by adding "\n" to the prompt sequence (this was
> > lost due to too many rebase cycles).
> > 3. Fix kconfig dependency for an intermediate patch (this is only useful in
> > case the latter patches are reverted).
> > 4. Add more accurate runtime debugger stub mechanism and remove old
> > ACPI_DEBUGGER_EXEC() stub mechanism.
> > 5. Add x86 acpi_os_readable() support.
> >
> > v3:
> > 1. Remove acpi_os_readable() support from this patchset. It's sent separately.
> > 2. Delete some deprecated comments.
> >
> > v4:
> > 1. Rebase patch 4/6 because of Kconfig changes.
> > 2. Include acpi_os_readable() back as no objections, also test result is
> > positive.
> >
> > Chen Yu (1):
> > ACPI / x86: introduce acpi_os_readable() support
> >
> > Colin Ian King (1):
> > ACPICA: Debugger: Remove unnecessary status check
> >
> > Lv Zheng (5):
> > ACPICA: Debugger: Convert some mechanisms to OSPM specific
> > ACPICA: Debugger: Fix runtime stub issues of ACPI_DEBUGGER_EXEC using
> > different stub mechanism
> > ACPI / debugger: Add IO interface to access debugger functionalities
> > tools/power/acpi: Add userspace AML interface support
> > ACPI / debugger: Add module support for ACPI debugger
>
> OK, I'm queuing up [1-6/7] for 4.5.
[Lv Zheng]
Thanks!

>
> The [7/7] needs at least to be looked at by the x86 maintainers.
[Lv Zheng]
OK, we'll re-send this patch to both x86/acpi mailing list with an additional test patch included to make it clearer.

Thanks and best regards
-Lv
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-15 06:13:31

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

Hi, Andy

> -----Original Message-----
> From: Andy Lutomirski [mailto:[email protected]]
> Sent: Tuesday, December 15, 2015 7:28 AM
> To: Zheng, Lv
> Cc: Wysocki, Rafael J; Brown, Len; Andy Lutomirski; Lv Zheng; linux-
> [email protected]; Linux ACPI; Chen, Yu C
> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support
>
> On Wed, Dec 2, 2015 at 6:43 PM, Lv Zheng <[email protected]> wrote:
> > From: Chen Yu <[email protected]>
> >
> > This patch implements acpi_os_readable(). The function is used by
> > ACPICA AML debugger to validate user specified pointers for dumping
> > the memory as ACPICA descriptor objects.
> >
[cut]
> >
> > +bool __acpi_memory_readable(void *pointer, size_t length) {
> > + unsigned long obj_start, obj_end;
> > + unsigned long start_pfn, end_pfn;
>
> What does "readable" mean in this context?
'readable' means : the address provided by the user,
is a dynamically allocated virtual address -
because the acpi address space are allocated by 'kmalloc',
acpi debugger must check if this address is a valid 'kmalloc'
address before accessing it.

This function does the sanity check that, the vitual address is a:
1. dynamically allocated address (beyond PAGE_OFFSET , but lower
than high_memory, VMALLOC_START, eg)
2. besides, the physical address must be direct-mapped(so it would not be a hole).

Thanks,
yu

????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-15 08:52:43

by Zheng, Lv

[permalink] [raw]
Subject: RE: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

Hi,

> From: Chen, Yu C
> Sent: Tuesday, December 15, 2015 2:13 PM
>
> Hi, Andy
>
> > From: Andy Lutomirski [mailto:[email protected]]
> > Sent: Tuesday, December 15, 2015 7:28 AM
> >
> > On Wed, Dec 2, 2015 at 6:43 PM, Lv Zheng <[email protected]> wrote:
> > > From: Chen Yu <[email protected]>
> > >
> > > This patch implements acpi_os_readable(). The function is used by
> > > ACPICA AML debugger to validate user specified pointers for dumping
> > > the memory as ACPICA descriptor objects.
> > >
> [cut]
> > >
> > > +bool __acpi_memory_readable(void *pointer, size_t length) {
> > > + unsigned long obj_start, obj_end;
> > > + unsigned long start_pfn, end_pfn;
> >
> > What does "readable" mean in this context?

[Lv Zheng]
The function is used by ACPICA "dump" command.
It accepts an arbitrary address, and tries to dump the memory block specified by the address as an acpi_object.
You can try: "help dump" in the interactive mode to confirm.
While acpi_object is actually all created by kmalloc.
So we just check if the specified memory block belongs to the kernel heap.
The readable/writeable is not so meaningful here as the kernel heap should always be both readable and writeable.

We do a lot of checks in this function in order to allow it to:
1. return true if "pointer" belongs to kernel heap when "length" is 0;
2. return false if "pointer" doesn't belong to kernel heap when "length" is 0;
3. return true if "pointer ~ pointer+length-1" belongs to a kernel heap range;
4. return false if "pointer ~ pointer+length-1" doesn't belong to any kernel heap range.

These checks are weak, but can help to avoid panics if users specify wrong pointers for the "dump" command.

> 'readable' means : the address provided by the user,
> is a dynamically allocated virtual address -
> because the acpi address space are allocated by 'kmalloc',
> acpi debugger must check if this address is a valid 'kmalloc'
> address before accessing it.
>
> This function does the sanity check that, the vitual address is a:
> 1. dynamically allocated address (beyond PAGE_OFFSET , but lower
> than high_memory, VMALLOC_START, eg)
> 2. besides, the physical address must be direct-mapped(so it would not be a
> hole).

[Lv Zheng]
There is a special case (possibly hackish) on x86_64.
x86_64 kernel maps kernel image twice.
One is called as high map and the other is called as low map.

Since we use __pa() to convert a virtual address,
If the virtual address belongs to the high map range, __pa() which takes care of converting high map addresses actually returns a physical address where there should also be low map mappings ready for it.
Thus the converted PFN from the result of __pa() will be treated as valid.

But this doesn't mean there is a high map for this virtual address.
x86_64 kernel drops several pages from high map in cleanup_highmap().
So accessing a virtual address that belongs to the holes whose page mappings have been dropped in this function could still result in panic due to no mappings.
By enforcing this check, we can avoid such a case.
Actually no acpi_object's virtual address will belong to high map range.

Thanks and best regards
-Lv
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-16 00:25:15

by Zheng, Lv

[permalink] [raw]
Subject: RE: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

Hi, Andy and Yu

> From: Zheng, Lv
> Sent: Tuesday, December 15, 2015 4:52 PM
>
> Hi,
>
> > From: Chen, Yu C
> > Sent: Tuesday, December 15, 2015 2:13 PM
> >
> > Hi, Andy
> >
> > > From: Andy Lutomirski [mailto:[email protected]]
> > > Sent: Tuesday, December 15, 2015 7:28 AM
> > >
> > > On Wed, Dec 2, 2015 at 6:43 PM, Lv Zheng <[email protected]> wrote:
> > > > From: Chen Yu <[email protected]>
> > > >
> > > > This patch implements acpi_os_readable(). The function is used by
> > > > ACPICA AML debugger to validate user specified pointers for dumping
> > > > the memory as ACPICA descriptor objects.
> > > >
> > [cut]
> > > >
> > > > +bool __acpi_memory_readable(void *pointer, size_t length) {
> > > > + unsigned long obj_start, obj_end;
> > > > + unsigned long start_pfn, end_pfn;
> > >
> > > What does "readable" mean in this context?
>
> [Lv Zheng]
> The function is used by ACPICA "dump" command.
> It accepts an arbitrary address, and tries to dump the memory block specified
> by the address as an acpi_object.
> You can try: "help dump" in the interactive mode to confirm.
> While acpi_object is actually all created by kmalloc.
[Lv Zheng]
This statement might be wrong, Let me Cc Bob to confirm.

Hi, Bob

Do we have statically allocated acpi_objects?
If we have, we need to change this patch to allow high map .data/.bss segments addresses to pass this check.

Thanks and best regards
-Lv

> So we just check if the specified memory block belongs to the kernel heap.
> The readable/writeable is not so meaningful here as the kernel heap should
> always be both readable and writeable.
>
> We do a lot of checks in this function in order to allow it to:
> 1. return true if "pointer" belongs to kernel heap when "length" is 0;
> 2. return false if "pointer" doesn't belong to kernel heap when "length" is 0;
> 3. return true if "pointer ~ pointer+length-1" belongs to a kernel heap range;
> 4. return false if "pointer ~ pointer+length-1" doesn't belong to any kernel heap
> range.
>
> These checks are weak, but can help to avoid panics if users specify wrong
> pointers for the "dump" command.
>
> > 'readable' means : the address provided by the user,
> > is a dynamically allocated virtual address -
> > because the acpi address space are allocated by 'kmalloc',
> > acpi debugger must check if this address is a valid 'kmalloc'
> > address before accessing it.
> >
> > This function does the sanity check that, the vitual address is a:
> > 1. dynamically allocated address (beyond PAGE_OFFSET , but lower
> > than high_memory, VMALLOC_START, eg)
> > 2. besides, the physical address must be direct-mapped(so it would not be a
> > hole).
>
> [Lv Zheng]
> There is a special case (possibly hackish) on x86_64.
> x86_64 kernel maps kernel image twice.
> One is called as high map and the other is called as low map.
>
> Since we use __pa() to convert a virtual address,
> If the virtual address belongs to the high map range, __pa() which takes care of
> converting high map addresses actually returns a physical address where there
> should also be low map mappings ready for it.
> Thus the converted PFN from the result of __pa() will be treated as valid.
>
> But this doesn't mean there is a high map for this virtual address.
> x86_64 kernel drops several pages from high map in cleanup_highmap().
> So accessing a virtual address that belongs to the holes whose page mappings
> have been dropped in this function could still result in panic due to no mappings.
> By enforcing this check, we can avoid such a case.
> Actually no acpi_object's virtual address will belong to high map range.
>
> Thanks and best regards
> -Lv
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-17 16:59:54

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

On Tue, Dec 15, 2015 at 4:25 PM, Zheng, Lv <[email protected]> wrote:
> Hi, Andy and Yu
>
>> From: Zheng, Lv
>> Sent: Tuesday, December 15, 2015 4:52 PM
>>
>> Hi,
>>
>> > From: Chen, Yu C
>> > Sent: Tuesday, December 15, 2015 2:13 PM
>> >
>> > Hi, Andy
>> >
>> > > From: Andy Lutomirski [mailto:[email protected]]
>> > > Sent: Tuesday, December 15, 2015 7:28 AM
>> > >
>> > > On Wed, Dec 2, 2015 at 6:43 PM, Lv Zheng <[email protected]> wrote:
>> > > > From: Chen Yu <[email protected]>
>> > > >
>> > > > This patch implements acpi_os_readable(). The function is used by
>> > > > ACPICA AML debugger to validate user specified pointers for dumping
>> > > > the memory as ACPICA descriptor objects.
>> > > >
>> > [cut]
>> > > >
>> > > > +bool __acpi_memory_readable(void *pointer, size_t length) {
>> > > > + unsigned long obj_start, obj_end;
>> > > > + unsigned long start_pfn, end_pfn;
>> > >
>> > > What does "readable" mean in this context?
>>
>> [Lv Zheng]
>> The function is used by ACPICA "dump" command.
>> It accepts an arbitrary address, and tries to dump the memory block specified
>> by the address as an acpi_object.
>> You can try: "help dump" in the interactive mode to confirm.
>> While acpi_object is actually all created by kmalloc.
> [Lv Zheng]
> This statement might be wrong, Let me Cc Bob to confirm.
>
> Hi, Bob
>
> Do we have statically allocated acpi_objects?
> If we have, we need to change this patch to allow high map .data/.bss segments addresses to pass this check.
>

I think that hpa or Borislav [cc'd] could address the memory map
details better than I could. However, this functionality seems
strange.

Are these physical addresses or virtual addresses that are being
dumped? In either case, ISTM that using something iike page_is_ram
might be a lot simpler.

--Andy

> Thanks and best regards
> -Lv
>
>> So we just check if the specified memory block belongs to the kernel heap.
>> The readable/writeable is not so meaningful here as the kernel heap should
>> always be both readable and writeable.
>>
>> We do a lot of checks in this function in order to allow it to:
>> 1. return true if "pointer" belongs to kernel heap when "length" is 0;
>> 2. return false if "pointer" doesn't belong to kernel heap when "length" is 0;
>> 3. return true if "pointer ~ pointer+length-1" belongs to a kernel heap range;
>> 4. return false if "pointer ~ pointer+length-1" doesn't belong to any kernel heap
>> range.
>>
>> These checks are weak, but can help to avoid panics if users specify wrong
>> pointers for the "dump" command.
>>
>> > 'readable' means : the address provided by the user,
>> > is a dynamically allocated virtual address -
>> > because the acpi address space are allocated by 'kmalloc',
>> > acpi debugger must check if this address is a valid 'kmalloc'
>> > address before accessing it.
>> >
>> > This function does the sanity check that, the vitual address is a:
>> > 1. dynamically allocated address (beyond PAGE_OFFSET , but lower
>> > than high_memory, VMALLOC_START, eg)
>> > 2. besides, the physical address must be direct-mapped(so it would not be a
>> > hole).
>>
>> [Lv Zheng]
>> There is a special case (possibly hackish) on x86_64.
>> x86_64 kernel maps kernel image twice.
>> One is called as high map and the other is called as low map.
>>
>> Since we use __pa() to convert a virtual address,
>> If the virtual address belongs to the high map range, __pa() which takes care of
>> converting high map addresses actually returns a physical address where there
>> should also be low map mappings ready for it.
>> Thus the converted PFN from the result of __pa() will be treated as valid.
>>
>> But this doesn't mean there is a high map for this virtual address.
>> x86_64 kernel drops several pages from high map in cleanup_highmap().
>> So accessing a virtual address that belongs to the holes whose page mappings
>> have been dropped in this function could still result in panic due to no mappings.
>> By enforcing this check, we can avoid such a case.
>> Actually no acpi_object's virtual address will belong to high map range.
>>
>> Thanks and best regards
>> -Lv



--
Andy Lutomirski
AMA Capital Management, LLC

2015-12-22 01:03:50

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

Hi Andy,
thanks for your review,

> -----Original Message-----
> From: Andy Lutomirski [mailto:[email protected]]
> Sent: Friday, December 18, 2015 1:00 AM
> To: Zheng, Lv
> Cc: Chen, Yu C; Moore, Robert; Wysocki, Rafael J; Brown, Len; Andy
> Lutomirski; Lv Zheng; [email protected]; Linux ACPI; H. Peter
> Anvin; Borislav Petkov
> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support
>
[cut]
>
> I think that hpa or Borislav [cc'd] could address the memory map details
> better than I could. However, this functionality seems strange.
>
> Are these physical addresses or virtual addresses that are being dumped?
[Yu] They are virtual addresses to be dumped.
> In either case, ISTM that using something iike page_is_ram might be a lot
> simpler.
[Yu] if i understand correctly, this API is used to check if the address is a valid
'kmalloc' style address, but not 'kmap' or 'vmalloc' address, and page_is_ram
might treat the latter as valid address?

thanks,
Yu
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-22 22:49:33

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

On Mon, Dec 21, 2015 at 5:03 PM, Chen, Yu C <[email protected]> wrote:
> Hi Andy,
> thanks for your review,
>
>> -----Original Message-----
>> From: Andy Lutomirski [mailto:[email protected]]
>> Sent: Friday, December 18, 2015 1:00 AM
>> To: Zheng, Lv
>> Cc: Chen, Yu C; Moore, Robert; Wysocki, Rafael J; Brown, Len; Andy
>> Lutomirski; Lv Zheng; [email protected]; Linux ACPI; H. Peter
>> Anvin; Borislav Petkov
>> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support
>>
> [cut]
>>
>> I think that hpa or Borislav [cc'd] could address the memory map details
>> better than I could. However, this functionality seems strange.
>>
>> Are these physical addresses or virtual addresses that are being dumped?
> [Yu] They are virtual addresses to be dumped.
>> In either case, ISTM that using something iike page_is_ram might be a lot
>> simpler.
> [Yu] if i understand correctly, this API is used to check if the address is a valid
> 'kmalloc' style address, but not 'kmap' or 'vmalloc' address, and page_is_ram
> might treat the latter as valid address?
>

I'm a bit puzzled as to why this matters, but I have no fundamental
objection to doing it that way.

What's the use case, though? That is, what goes wrong if the function
just always returns false?

--Andy

> thanks,
> Yu



--
Andy Lutomirski
AMA Capital Management, LLC

2015-12-23 03:25:20

by Zheng, Lv

[permalink] [raw]
Subject: RE: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

Hi, Andy

> From: [email protected] [mailto:linux-acpi-
> [email protected]] On Behalf Of Andy Lutomirski
> Sent: Wednesday, December 23, 2015 6:49 AM
> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support
>
> On Mon, Dec 21, 2015 at 5:03 PM, Chen, Yu C <[email protected]> wrote:
> > Hi Andy,
> > thanks for your review,
> >
> >> -----Original Message-----
> >> From: Andy Lutomirski [mailto:[email protected]]
> >> Sent: Friday, December 18, 2015 1:00 AM
> >> To: Zheng, Lv
> >> Cc: Chen, Yu C; Moore, Robert; Wysocki, Rafael J; Brown, Len; Andy
> >> Lutomirski; Lv Zheng; [email protected]; Linux ACPI; H. Peter
> >> Anvin; Borislav Petkov
> >> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable()
> support
> >>
> > [cut]
> >>
> >> I think that hpa or Borislav [cc'd] could address the memory map details
> >> better than I could. However, this functionality seems strange.
> >>
> >> Are these physical addresses or virtual addresses that are being dumped?
> > [Yu] They are virtual addresses to be dumped.
> >> In either case, ISTM that using something iike page_is_ram might be a lot
> >> simpler.
> > [Yu] if i understand correctly, this API is used to check if the address is a valid
> > 'kmalloc' style address, but not 'kmap' or 'vmalloc' address, and page_is_ram
> > might treat the latter as valid address?
> >
>
> I'm a bit puzzled as to why this matters, but I have no fundamental objection to doing it that way.
[Lv Zheng]
IMO, using page_is_ram() or something similar, the problem is what we need to solve in the current approach still need to be solved:
1. How can we convert a virtual address into a "struct page"?
There is no kernel API to convert any virtual address into struct page.
Even there is such a kernel API to convert kmap/vmalloc addresses, we still couldn't use it.
Because if we want to validate kmap/vmaloc pages, we need 2 APIs rather than 1 API while ACPICA only provides 1 API for this purpose.
The 2 APIs should be get/put style to ping the page mappings as the mappings other than the direct mappings will not be stationary in the kernel address space.
Fortunately we needn't take care of the mappings other than the direct mappings (reasons are in the 2nd comment).
So we still need to use the direct mapping APIs here.
2. How can we ensure the page is a direct mapping page?
I think Yu should confirm if there is such a common kernel API.
If there is such an API, we should use it so that we can remove the arch specific stuffs.

> What's the use case, though?
[Lv Zheng]
Fortunately, currently ACPICA only uses this API to validate if a namespace node, an operand object or a parser object is readable.
See drivers/acpi/acpica/dbdisplay.c and drivers/acpi/acpica/dbcmds.c.

> That is, what goes wrong if the function just always returns false?
[Lv Zheng]
1. If it always returns false, then many ACPICA debugger internal object conversion/dump functionalities won't be functioning.
For example, you can try to type “dump \_SB" in acpidbg shell and it will return an error:
"Invalid named object at address xxxxxxxxxxxxxxxx"
2. While if this function always returns true (current linux-pm/linux-next merged stuffs), we can see such a result:
Object (ffffxxxxxxxxxxxx) Pathname: \_SB
Name : _SB_
Type : 06 [Device]
...
3. But if it always returns true, then there will be another problem:
User can type an invalid address, for example, "dump 0xFFFFFFFFFFFFFFFF".
And ACPICA debugger will try to access this invalid virtual address and finally result in a panic.
So we need to implement acpi_os_readable() to harden the check.

[Lv Zheng]
Let me say more about this patch.
Currently this patch looks wrong.
Though, most of the acpi_object(s) are kmalloced in the kernel heap, as far as I know, at least the namespace root is a statically allocated object in ACPICA.
Maybe "One"/"Ones"/"Zero" operands are all statically allocated objects.

So we need to modify this function to return true for the addresses that belong to .data/.bss sections for x86_64 kernels.
You can confirm this by typing "dump \" in the acpidbg shell, it now returns:
"Invalid named object at address ffffffff8xxxxxxx".
We'll update it and send it after testing.

Thanks and best regards
-Lv
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-24 01:40:53

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

On Tue, Dec 22, 2015 at 7:25 PM, Zheng, Lv <[email protected]> wrote:
> Hi, Andy
>
>> From: [email protected] [mailto:linux-acpi-
>> [email protected]] On Behalf Of Andy Lutomirski
>> Sent: Wednesday, December 23, 2015 6:49 AM
>> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support
>>
>> On Mon, Dec 21, 2015 at 5:03 PM, Chen, Yu C <[email protected]> wrote:
>> > Hi Andy,
>> > thanks for your review,
>> >
>> >> -----Original Message-----
>> >> From: Andy Lutomirski [mailto:[email protected]]
>> >> Sent: Friday, December 18, 2015 1:00 AM
>> >> To: Zheng, Lv
>> >> Cc: Chen, Yu C; Moore, Robert; Wysocki, Rafael J; Brown, Len; Andy
>> >> Lutomirski; Lv Zheng; [email protected]; Linux ACPI; H. Peter
>> >> Anvin; Borislav Petkov
>> >> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable()
>> support
>> >>
>> > [cut]
>> >>
>> >> I think that hpa or Borislav [cc'd] could address the memory map details
>> >> better than I could. However, this functionality seems strange.
>> >>
>> >> Are these physical addresses or virtual addresses that are being dumped?
>> > [Yu] They are virtual addresses to be dumped.
>> >> In either case, ISTM that using something iike page_is_ram might be a lot
>> >> simpler.
>> > [Yu] if i understand correctly, this API is used to check if the address is a valid
>> > 'kmalloc' style address, but not 'kmap' or 'vmalloc' address, and page_is_ram
>> > might treat the latter as valid address?
>> >
>>
>> I'm a bit puzzled as to why this matters, but I have no fundamental objection to doing it that way.
> [Lv Zheng]
> IMO, using page_is_ram() or something similar, the problem is what we need to solve in the current approach still need to be solved:
> 1. How can we convert a virtual address into a "struct page"?
> There is no kernel API to convert any virtual address into struct page.
> Even there is such a kernel API to convert kmap/vmalloc addresses, we still couldn't use it.
> Because if we want to validate kmap/vmaloc pages, we need 2 APIs rather than 1 API while ACPICA only provides 1 API for this purpose.
> The 2 APIs should be get/put style to ping the page mappings as the mappings other than the direct mappings will not be stationary in the kernel address space.
> Fortunately we needn't take care of the mappings other than the direct mappings (reasons are in the 2nd comment).
> So we still need to use the direct mapping APIs here.
> 2. How can we ensure the page is a direct mapping page?
> I think Yu should confirm if there is such a common kernel API.
> If there is such an API, we should use it so that we can remove the arch specific stuffs.
>
>> What's the use case, though?
> [Lv Zheng]
> Fortunately, currently ACPICA only uses this API to validate if a namespace node, an operand object or a parser object is readable.
> See drivers/acpi/acpica/dbdisplay.c and drivers/acpi/acpica/dbcmds.c.
>
>> That is, what goes wrong if the function just always returns false?
> [Lv Zheng]
> 1. If it always returns false, then many ACPICA debugger internal object conversion/dump functionalities won't be functioning.
> For example, you can try to type “dump \_SB" in acpidbg shell and it will return an error:
> "Invalid named object at address xxxxxxxxxxxxxxxx"
> 2. While if this function always returns true (current linux-pm/linux-next merged stuffs), we can see such a result:
> Object (ffffxxxxxxxxxxxx) Pathname: \_SB
> Name : _SB_
> Type : 06 [Device]
>

It seems a bit unfortunate to me that the ACPICA debugger lets
userspace choose what address to dump rather than dumping by pathname,
but given that constraint, I guess this function is needed.

Can you do something like checking virt_addr_valid and then using
virt_to_pfn and page_is_ram? If that's not enough (e.g. if it doesn't
work for vmalloc addresses and you need those), you could try to do
something like slow_virt_to_phys, but you'd need to do some extra
checks to avoid the BUG in the function.

--Andy

2015-12-24 06:16:32

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH] ACPI / debugger: Fix an issue a flag is modified without locking

There is one line of code, executed out of locking due to rebase mistakes.
This patch fixes this issue.

Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpi_dbg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
index 381beb2..fa18bd0 100644
--- a/drivers/acpi/acpi_dbg.c
+++ b/drivers/acpi/acpi_dbg.c
@@ -516,10 +516,10 @@ static int acpi_aml_open(struct inode *inode, struct file *file)
ret = -EINVAL;
goto err_lock;
}
- acpi_aml_io.flags |= ACPI_AML_OPENED;
pr_debug("Debugger thread initialized.\n");

mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags |= ACPI_AML_OPENED;
acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0;
acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0;
pr_debug("Debugger interface opened.\n");
--
1.7.10

2015-12-24 07:57:55

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

Hi,Andy

> -----Original Message-----
> From: Andy Lutomirski [mailto:[email protected]]
> Sent: Thursday, December 24, 2015 9:40 AM
> To: Zheng, Lv
> Cc: Chen, Yu C; Moore, Robert; Wysocki, Rafael J; Brown, Len; Andy
> Lutomirski; Lv Zheng; [email protected]; Linux ACPI; H. Peter
> Anvin; Borislav Petkov
> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support
>
> On Tue, Dec 22, 2015 at 7:25 PM, Zheng, Lv <[email protected]> wrote:
[cut]
>
> It seems a bit unfortunate to me that the ACPICA debugger lets userspace
> choose what address to dump rather than dumping by pathname, but given
> that constraint, I guess this function is needed.
>
> Can you do something like checking virt_addr_valid and then using
> virt_to_pfn and page_is_ram? If that's not enough (e.g. if it doesn't work for
> vmalloc addresses and you need those), you could try to do something like
> slow_virt_to_phys, but you'd need to do some extra checks to avoid the
> BUG in the function.
>
[Yu] This is a good method, firstly virt_addr_valid can exclude the vaddr of vmalloc/kmap
, then slow_virt_to_phys can check if this vaddr has a valid pte physical address, but I guess
we need another non-BUG_on version of slow_virt_to_phys, I'll try to test with your suggestion,
thanks!
Yu


????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-24 08:01:40

by Chen Yu

[permalink] [raw]
Subject: RE: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support

(resend for messy code)
Hi, Andy

> -----Original Message-----
> From: Andy Lutomirski [mailto:[email protected]]
> Sent: Thursday, December 24, 2015 9:40 AM
> To: Zheng, Lv
> Cc: Chen, Yu C; Moore, Robert; Wysocki, Rafael J; Brown, Len; Andy
> Lutomirski; Lv Zheng; [email protected]; Linux ACPI; H. Peter
> Anvin; Borislav Petkov
> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable() support
>
> On Tue, Dec 22, 2015 at 7:25 PM, Zheng, Lv <[email protected]> wrote:
> > Hi, Andy
> >
> >> From: [email protected] [mailto:linux-acpi-
> >> [email protected]] On Behalf Of Andy Lutomirski
> >> Sent: Wednesday, December 23, 2015 6:49 AM
> >> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce acpi_os_readable()
> >> support
> >>
> >> On Mon, Dec 21, 2015 at 5:03 PM, Chen, Yu C <[email protected]>
> wrote:
> >> > Hi Andy,
> >> > thanks for your review,
> >> >
> >> >> -----Original Message-----
> >> >> From: Andy Lutomirski [mailto:[email protected]]
> >> >> Sent: Friday, December 18, 2015 1:00 AM
> >> >> To: Zheng, Lv
> >> >> Cc: Chen, Yu C; Moore, Robert; Wysocki, Rafael J; Brown, Len; Andy
> >> >> Lutomirski; Lv Zheng; [email protected]; Linux ACPI; H.
> >> >> Peter Anvin; Borislav Petkov
> >> >> Subject: Re: [PATCH v4 7/7] ACPI / x86: introduce
> >> >> acpi_os_readable()
> >> support
> >> >>
> >> > [cut]
> >> >>
> >> >> I think that hpa or Borislav [cc'd] could address the memory map
> >> >> details better than I could. However, this functionality seems strange.
> >> >>
> >> >> Are these physical addresses or virtual addresses that are being
> dumped?
> >> > [Yu] They are virtual addresses to be dumped.
> >> >> In either case, ISTM that using something iike page_is_ram might
> >> >> be a lot simpler.
> >> > [Yu] if i understand correctly, this API is used to check if the
> >> > address is a valid 'kmalloc' style address, but not 'kmap' or
> >> > 'vmalloc' address, and page_is_ram might treat the latter as valid
> address?
> >> >
> >>
> >> I'm a bit puzzled as to why this matters, but I have no fundamental
> objection to doing it that way.
> > [Lv Zheng]
> > IMO, using page_is_ram() or something similar, the problem is what we
> need to solve in the current approach still need to be solved:
> > 1. How can we convert a virtual address into a "struct page"?
> > There is no kernel API to convert any virtual address into struct page.
> > Even there is such a kernel API to convert kmap/vmalloc addresses, we
> still couldn't use it.
> > Because if we want to validate kmap/vmaloc pages, we need 2 APIs
> rather than 1 API while ACPICA only provides 1 API for this purpose.
> > The 2 APIs should be get/put style to ping the page mappings as the
> mappings other than the direct mappings will not be stationary in the kernel
> address space.
> > Fortunately we needn't take care of the mappings other than the direct
> mappings (reasons are in the 2nd comment).
> > So we still need to use the direct mapping APIs here.
> > 2. How can we ensure the page is a direct mapping page?
> > I think Yu should confirm if there is such a common kernel API.
> > If there is such an API, we should use it so that we can remove the arch
> specific stuffs.
> >
> >> What's the use case, though?
> > [Lv Zheng]
> > Fortunately, currently ACPICA only uses this API to validate if a namespace
> node, an operand object or a parser object is readable.
> > See drivers/acpi/acpica/dbdisplay.c and drivers/acpi/acpica/dbcmds.c.
> >
> >> That is, what goes wrong if the function just always returns false?
> > [Lv Zheng]
> > 1. If it always returns false, then many ACPICA debugger internal object
> conversion/dump functionalities won't be functioning.
> > For example, you can try to type “dump \_SB" in acpidbg shell and it will
> return an error:
> > "Invalid named object at address xxxxxxxxxxxxxxxx"
> > 2. While if this function always returns true (current linux-pm/linux-next
> merged stuffs), we can see such a result:
> > Object (ffffxxxxxxxxxxxx) Pathname: \_SB
> > Name : _SB_
> > Type : 06 [Device]
> >
>
> It seems a bit unfortunate to me that the ACPICA debugger lets userspace
> choose what address to dump rather than dumping by pathname, but given
> that constraint, I guess this function is needed.
>
> Can you do something like checking virt_addr_valid and then using
> virt_to_pfn and page_is_ram? If that's not enough (e.g. if it doesn't work for
> vmalloc addresses and you need those), you could try to do something like
> slow_virt_to_phys, but you'd need to do some extra checks to avoid the
> BUG in the function.
>
[Yu] This is a good method, firstly virt_addr_valid can exclude the vaddr of vmalloc/kmap ,
then slow_virt_to_phys can check if this vaddr has a valid pte physical address,
but I guess we need another non-BUG_on version of slow_virt_to_phys,
I'll try to test with your suggestion, thanks!
Yu


????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-12-25 03:22:56

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH] ACPI / debugger: Fix a redundant mutex unlock issue in acpi_aml_open()

Fix a double mutex_unlock() issue where acpi_initialize_debugger() is
called with the mutex already unlocked.

Reported-by: Dan Carpenter <[email protected]>
Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/acpi_dbg.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
index fa18bd0..9c31b08 100644
--- a/drivers/acpi/acpi_dbg.c
+++ b/drivers/acpi/acpi_dbg.c
@@ -514,7 +514,7 @@ static int acpi_aml_open(struct inode *inode, struct file *file)
if (ACPI_FAILURE(status)) {
pr_err("Failed to initialize debugger.\n");
ret = -EINVAL;
- goto err_lock;
+ goto err_exit;
}
pr_debug("Debugger thread initialized.\n");

@@ -531,6 +531,7 @@ err_lock:
acpi_aml_active_reader = NULL;
}
mutex_unlock(&acpi_aml_io.lock);
+err_exit:
return ret;
}

--
1.7.10