Commit b17629db authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'acpi-debug' into acpica

parents 74bf8efb 59adb398
...@@ -58,14 +58,25 @@ config ACPI_CCA_REQUIRED ...@@ -58,14 +58,25 @@ config ACPI_CCA_REQUIRED
bool bool
config ACPI_DEBUGGER config ACPI_DEBUGGER
bool "AML debugger interface (EXPERIMENTAL)" bool "AML debugger interface"
select ACPI_DEBUG select ACPI_DEBUG
help help
Enable in-kernel debugging of AML facilities: statistics, internal Enable in-kernel debugging of AML facilities: statistics,
object dump, single step control method execution. internal object dump, single step control method execution.
This is still under development, currently enabling this only This is still under development, currently enabling this only
results in the compilation of the ACPICA debugger files. 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 config ACPI_SLEEP
bool bool
depends on SUSPEND || HIBERNATION depends on SUSPEND || HIBERNATION
......
...@@ -79,6 +79,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o ...@@ -79,6 +79,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.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 has its own "processor." module_param namespace
processor-y := processor_driver.o processor-y := processor_driver.o
......
/*
* ACPI AML interfacing support
*
* Copyright (C) 2015, Intel Corporation
* Authors: Lv Zheng <lv.zheng@intel.com>
*
* 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.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.
*/
static 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;
}
/*
* 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.
*/
static 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;
}
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.
*/
static 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;
}
static int acpi_aml_wait_command_ready(bool single_step,
char *buffer, size_t length)
{
acpi_status status;
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(buffer, length, NULL);
if (ACPI_FAILURE(status))
return -EINVAL;
return 0;
}
static int acpi_aml_notify_command_complete(void)
{
return 0;
}
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_exit;
}
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");
}
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);
err_exit:
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));
if (copy_to_user(buf, p, n)) {
ret = -EFAULT;
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 (!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));
if (copy_from_user(p, buf, n)) {
ret = -EFAULT;
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 (!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,
};
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)
{
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);
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) {
ret = -ENODEV;
goto err_exit;
}
ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger);
if (ret)
goto err_fs;
acpi_aml_initialized = true;
err_fs:
if (ret) {
debugfs_remove(acpi_aml_dentry);
acpi_aml_dentry = NULL;
}
err_exit:
return ret;
}
void __exit acpi_aml_exit(void)
{
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);
MODULE_AUTHOR("Lv Zheng");
MODULE_DESCRIPTION("ACPI debugger userspace IO driver");
MODULE_LICENSE("GPL");
...@@ -80,9 +80,15 @@ struct acpi_db_execute_walk { ...@@ -80,9 +80,15 @@ struct acpi_db_execute_walk {
/* /*
* dbxface - external debugger interfaces * dbxface - external debugger interfaces
*/ */
acpi_status ACPI_DBR_DEPENDENT_RETURN_OK(acpi_status
acpi_db_single_step(struct acpi_walk_state *walk_state, acpi_db_single_step(struct acpi_walk_state
union acpi_parse_object *op, u32 op_type); *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 * dbcmds - debug commands and output routines
...@@ -182,11 +188,15 @@ void acpi_db_display_method_info(union acpi_parse_object *op); ...@@ -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_decode_and_display_object(char *target, char *output_type);
void ACPI_DBR_DEPENDENT_RETURN_VOID(void
acpi_db_display_result_object(union acpi_operand_object *obj_desc, acpi_db_display_result_object(union
struct acpi_walk_state *walk_state); 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); void acpi_db_display_arguments(void);
...@@ -198,9 +208,13 @@ void acpi_db_display_calling_tree(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_object_type(char *object_arg);
void ACPI_DBR_DEPENDENT_RETURN_VOID(void
acpi_db_display_argument_object(union acpi_operand_object *obj_desc, acpi_db_display_argument_object(union
struct acpi_walk_state *walk_state); acpi_operand_object
*obj_desc,
struct
acpi_walk_state
*walk_state))
/* /*
* dbexec - debugger control method execution * dbexec - debugger control method execution
...@@ -257,7 +271,7 @@ acpi_db_command_dispatch(char *input_buffer, ...@@ -257,7 +271,7 @@ acpi_db_command_dispatch(char *input_buffer,
void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context); 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 *acpi_db_get_next_token(char *string,
char **next, acpi_object_type * return_type); char **next, acpi_object_type * return_type);
......
...@@ -326,7 +326,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list); ...@@ -326,7 +326,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list);
#ifdef ACPI_DEBUGGER #ifdef ACPI_DEBUGGER
ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE); 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_INIT_GLOBAL(acpi_thread_id, acpi_gbl_db_thread_id, ACPI_INVALID_THREAD_ID);
ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods); ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods);
...@@ -345,7 +344,6 @@ ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]); ...@@ -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 */ /* 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_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_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[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); ...@@ -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_nodes);
ACPI_GLOBAL(u32, acpi_gbl_num_objects); 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 */ #endif /* ACPI_DEBUGGER */
/***************************************************************************** /*****************************************************************************
......
...@@ -400,17 +400,6 @@ ...@@ -400,17 +400,6 @@
#define ACPI_HW_OPTIONAL_FUNCTION(addr) NULL #define ACPI_HW_OPTIONAL_FUNCTION(addr) NULL
#endif #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 * Macros used for ACPICA utilities only
*/ */
......
...@@ -679,6 +679,12 @@ acpi_db_display_result_object(union acpi_operand_object *obj_desc, ...@@ -679,6 +679,12 @@ acpi_db_display_result_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state) 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 */ /* Only display if single stepping */
if (!acpi_gbl_cm_single_step) { if (!acpi_gbl_cm_single_step) {
...@@ -708,6 +714,12 @@ acpi_db_display_argument_object(union acpi_operand_object *obj_desc, ...@@ -708,6 +714,12 @@ acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state) 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) { if (!acpi_gbl_cm_single_step) {
return; return;
} }
......
...@@ -53,8 +53,6 @@ static u32 acpi_db_get_line(char *input_buffer); ...@@ -53,8 +53,6 @@ static u32 acpi_db_get_line(char *input_buffer);
static u32 acpi_db_match_command(char *user_command); 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_command_info(char *command, u8 display_all);
static void acpi_db_display_help(char *command); static void acpi_db_display_help(char *command);
...@@ -1149,55 +1147,16 @@ acpi_db_command_dispatch(char *input_buffer, ...@@ -1149,55 +1147,16 @@ acpi_db_command_dispatch(char *input_buffer,
void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context) 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; 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 * FUNCTION: acpi_db_user_commands
* *
* PARAMETERS: prompt - User prompt (depends on mode) * PARAMETERS: None
* op - Current executing parse op
* *
* RETURN: None * RETURN: None
* *
...@@ -1206,7 +1165,7 @@ static void acpi_db_single_thread(void) ...@@ -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; acpi_status status = AE_OK;
...@@ -1216,52 +1175,31 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op) ...@@ -1216,52 +1175,31 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
while (!acpi_gbl_db_terminate_loop) { while (!acpi_gbl_db_terminate_loop) {
/* Force output to console until a command is entered */ /* Wait the readiness of the command */
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
/* Different prompt if method is executing */ status = acpi_os_wait_command_ready();
if (ACPI_FAILURE(status)) {
if (!acpi_gbl_method_executing) { break;
acpi_os_printf("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
} else {
acpi_os_printf("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
} }
/* Get the user input line */ /* Just call to the command line interpreter */
status = acpi_os_get_line(acpi_gbl_db_line_buf, acpi_gbl_method_executing = FALSE;
ACPI_DB_LINE_BUFFER_SIZE, NULL); acpi_gbl_step_to_next_call = FALSE;
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"While parsing command line"));
return (status);
}
/* Check for single or multithreaded debug */ (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL,
NULL);
if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { /* Notify the completion of the command */
/*
* Signal the debug thread that we have a command to execute,
* and wait for the command to complete.
*/
acpi_os_release_mutex(acpi_gbl_db_command_ready);
if (ACPI_FAILURE(status)) {
return (status);
}
status = status = acpi_os_notify_command_complete();
acpi_os_acquire_mutex(acpi_gbl_db_command_complete,
ACPI_WAIT_FOREVER);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return (status); break;
}
} else {
/* Just call to the command line interpreter */
acpi_db_single_thread();
} }
} }
if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
ACPI_EXCEPTION((AE_INFO, status, "While parsing command line"));
}
return (status); return (status);
} }
...@@ -85,46 +85,21 @@ acpi_db_start_command(struct acpi_walk_state *walk_state, ...@@ -85,46 +85,21 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,
acpi_gbl_method_executing = TRUE; acpi_gbl_method_executing = TRUE;
status = AE_CTRL_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 */ while (status == AE_CTRL_TRUE) {
acpi_os_release_mutex(acpi_gbl_db_command_complete); /* Notify the completion of the command */
status = status = acpi_os_notify_command_complete();
acpi_os_acquire_mutex(acpi_gbl_db_command_ready,
ACPI_WAIT_FOREVER);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return (status); goto error_exit;
}
} else {
/* Single threaded, we must get a command line ourselves */
/* Force output to console until a command is entered */
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
/* Different prompt if method is executing */
if (!acpi_gbl_method_executing) {
acpi_os_printf("%1c ",
ACPI_DEBUGGER_COMMAND_PROMPT);
} else {
acpi_os_printf("%1c ",
ACPI_DEBUGGER_EXECUTE_PROMPT);
} }
/* Get the user input line */ /* Wait the readiness of the command */
status = acpi_os_get_line(acpi_gbl_db_line_buf, status = acpi_os_wait_command_ready();
ACPI_DB_LINE_BUFFER_SIZE,
NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, goto error_exit;
"While parsing command line"));
return (status);
}
} }
status = status =
...@@ -134,9 +109,44 @@ acpi_db_start_command(struct acpi_walk_state *walk_state, ...@@ -134,9 +109,44 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,
/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */ /* 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); return (status);
} }
/*******************************************************************************
*
* 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 * FUNCTION: acpi_db_single_step
...@@ -420,15 +430,7 @@ acpi_status acpi_initialize_debugger(void) ...@@ -420,15 +430,7 @@ acpi_status acpi_initialize_debugger(void)
/* These were created with one unit, grab it */ /* These were created with one unit, grab it */
status = acpi_os_acquire_mutex(acpi_gbl_db_command_complete, status = acpi_os_initialize_command_signals();
ACPI_WAIT_FOREVER);
if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get debugger mutex\n");
return_ACPI_STATUS(status);
}
status = acpi_os_acquire_mutex(acpi_gbl_db_command_ready,
ACPI_WAIT_FOREVER);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get debugger mutex\n"); acpi_os_printf("Could not get debugger mutex\n");
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
...@@ -473,13 +475,14 @@ void acpi_terminate_debugger(void) ...@@ -473,13 +475,14 @@ void acpi_terminate_debugger(void)
acpi_gbl_db_terminate_loop = TRUE; acpi_gbl_db_terminate_loop = TRUE;
if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {
acpi_os_release_mutex(acpi_gbl_db_command_ready);
/* Wait the AML Debugger threads */ /* Wait the AML Debugger threads */
while (!acpi_gbl_db_threads_terminated) { while (!acpi_gbl_db_threads_terminated) {
acpi_os_sleep(100); acpi_os_sleep(100);
} }
acpi_os_terminate_command_signals();
} }
if (acpi_gbl_db_buffer) { if (acpi_gbl_db_buffer) {
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include "amlcode.h" #include "amlcode.h"
#include "acdispat.h" #include "acdispat.h"
#include "acinterp.h" #include "acinterp.h"
#include "acdebug.h"
#define _COMPONENT ACPI_DISPATCHER #define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME("dscontrol") ACPI_MODULE_NAME("dscontrol")
...@@ -348,14 +349,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, ...@@ -348,14 +349,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state,
case AML_BREAK_POINT_OP: case AML_BREAK_POINT_OP:
/* acpi_db_signal_break_point(walk_state);
* 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"));
/* Call to the OSL in case OS wants a piece of the action */ /* Call to the OSL in case OS wants a piece of the action */
......
...@@ -605,8 +605,8 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state, ...@@ -605,8 +605,8 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(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 { } else {
/* Check for null name case */ /* Check for null name case */
...@@ -638,10 +638,11 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state, ...@@ -638,10 +638,11 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"Argument previously created, already stacked\n")); "Argument previously created, already stacked\n"));
ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object acpi_db_display_argument_object(walk_state->
(walk_state-> operands[walk_state->
operands[walk_state->num_operands - num_operands -
1], walk_state)); 1],
walk_state);
/* /*
* Use value that was already previously returned * Use value that was already previously returned
...@@ -685,8 +686,7 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state, ...@@ -685,8 +686,7 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object acpi_db_display_argument_object(obj_desc, walk_state);
(obj_desc, walk_state));
} }
return_ACPI_STATUS(AE_OK); return_ACPI_STATUS(AE_OK);
......
...@@ -178,8 +178,7 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state, ...@@ -178,8 +178,7 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state,
/* Break to debugger to display result */ /* Break to debugger to display result */
ACPI_DEBUGGER_EXEC(acpi_db_display_result_object acpi_db_display_result_object(local_obj_desc, walk_state);
(local_obj_desc, walk_state));
/* /*
* Delete the predicate result object (we know that * 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) ...@@ -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) */ /* Call debugger for single step support (DEBUG build only) */
ACPI_DEBUGGER_EXEC(status = status = acpi_db_single_step(walk_state, op, op_class);
acpi_db_single_step(walk_state, op, op_class)); if (ACPI_FAILURE(status)) {
ACPI_DEBUGGER_EXEC(if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status);
return_ACPI_STATUS(status);} }
) ;
/* Decode the Opcode Class */ /* Decode the Opcode Class */
...@@ -728,8 +726,8 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state) ...@@ -728,8 +726,8 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
/* Break to debugger to display result */ /* Break to debugger to display result */
ACPI_DEBUGGER_EXEC(acpi_db_display_result_object acpi_db_display_result_object(walk_state->result_obj,
(walk_state->result_obj, walk_state)); walk_state);
/* /*
* Delete the result op if and only if: * Delete the result op if and only if:
......
...@@ -111,17 +111,6 @@ acpi_status acpi_ut_mutex_initialize(void) ...@@ -111,17 +111,6 @@ acpi_status acpi_ut_mutex_initialize(void)
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(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); return_ACPI_STATUS(status);
} }
...@@ -162,12 +151,6 @@ void acpi_ut_mutex_terminate(void) ...@@ -162,12 +151,6 @@ void acpi_ut_mutex_terminate(void)
/* Delete the reader/writer lock */ /* Delete the reader/writer lock */
acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_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; return_VOID;
} }
......
...@@ -1094,6 +1094,7 @@ static int __init acpi_init(void) ...@@ -1094,6 +1094,7 @@ static int __init acpi_init(void)
acpi_debugfs_init(); acpi_debugfs_init();
acpi_sleep_proc_init(); acpi_sleep_proc_init();
acpi_wakeup_device_init(); acpi_wakeup_device_init();
acpi_debugger_init();
return 0; return 0;
} }
......
...@@ -220,6 +220,7 @@ void acpi_os_printf(const char *fmt, ...) ...@@ -220,6 +220,7 @@ void acpi_os_printf(const char *fmt, ...)
acpi_os_vprintf(fmt, args); acpi_os_vprintf(fmt, args);
va_end(args); va_end(args);
} }
EXPORT_SYMBOL(acpi_os_printf);
void acpi_os_vprintf(const char *fmt, va_list args) void acpi_os_vprintf(const char *fmt, va_list args)
{ {
...@@ -234,6 +235,7 @@ void acpi_os_vprintf(const char *fmt, va_list args) ...@@ -234,6 +235,7 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer); printk(KERN_CONT "%s", buffer);
} }
#else #else
if (acpi_debugger_write_log(buffer) < 0)
printk(KERN_CONT "%s", buffer); printk(KERN_CONT "%s", buffer);
#endif #endif
} }
...@@ -1101,6 +1103,200 @@ static void acpi_os_execute_deferred(struct work_struct *work) ...@@ -1101,6 +1103,200 @@ static void acpi_os_execute_deferred(struct work_struct *work)
kfree(dpc); 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 * FUNCTION: acpi_os_execute
...@@ -1127,6 +1323,15 @@ acpi_status acpi_os_execute(acpi_execute_type type, ...@@ -1127,6 +1323,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
"Scheduling function [%p(%p)] for deferred execution.\n", "Scheduling function [%p(%p)] for deferred execution.\n",
function, context)); function, context));
if (type == OSL_DEBUGGER_MAIN_THREAD) {
ret = acpi_debugger_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 * Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the work_struct list in a * freed by the callee. The kernel handles the work_struct list in a
...@@ -1151,11 +1356,17 @@ acpi_status acpi_os_execute(acpi_execute_type type, ...@@ -1151,11 +1356,17 @@ acpi_status acpi_os_execute(acpi_execute_type type,
if (type == OSL_NOTIFY_HANDLER) { if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq; queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred); INIT_WORK(&dpc->work, acpi_os_execute_deferred);
} else { } else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq; queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred); 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 * 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 * the SMI runs on CPU 0. An SMI can be initiated by any AML, but
...@@ -1164,13 +1375,15 @@ acpi_status acpi_os_execute(acpi_execute_type type, ...@@ -1164,13 +1375,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* queueing on CPU 0. * queueing on CPU 0.
*/ */
ret = queue_work_on(0, queue, &dpc->work); ret = queue_work_on(0, queue, &dpc->work);
if (!ret) { if (!ret) {
printk(KERN_ERR PREFIX printk(KERN_ERR PREFIX
"Call to queue_work() failed.\n"); "Call to queue_work() failed.\n");
status = AE_ERROR; status = AE_ERROR;
kfree(dpc);
} }
err_workqueue:
if (ACPI_FAILURE(status))
kfree(dpc);
out_thread:
return status; return status;
} }
EXPORT_SYMBOL(acpi_os_execute); EXPORT_SYMBOL(acpi_os_execute);
...@@ -1358,10 +1571,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read) ...@@ -1358,10 +1571,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
chars = strlen(buffer) - 1; chars = strlen(buffer) - 1;
buffer[chars] = '\0'; buffer[chars] = '\0';
} }
#else
int ret;
ret = acpi_debugger_read_cmd(buffer, buffer_length);
if (ret < 0)
return AE_ERROR;
if (bytes_read)
*bytes_read = ret;
#endif #endif
return AE_OK; return AE_OK;
} }
EXPORT_SYMBOL(acpi_os_get_line);
acpi_status acpi_os_wait_command_ready(void)
{
int ret;
ret = acpi_debugger_wait_command_ready();
if (ret < 0)
return AE_ERROR;
return AE_OK;
}
acpi_status acpi_os_notify_command_complete(void)
{
int ret;
ret = acpi_debugger_notify_command_complete();
if (ret < 0)
return AE_ERROR;
return AE_OK;
}
acpi_status acpi_os_signal(u32 function, void *info) acpi_status acpi_os_signal(u32 function, void *info)
{ {
......
...@@ -349,12 +349,28 @@ void acpi_os_redirect_output(void *destination); ...@@ -349,12 +349,28 @@ void acpi_os_redirect_output(void *destination);
#endif #endif
/* /*
* Debug input * Debug IO
*/ */
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line
acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read); acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read);
#endif #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) * Obtain ACPI table(s)
*/ */
......
...@@ -263,6 +263,15 @@ ACPI_INIT_GLOBAL(u32, acpi_gbl_trace_dbg_layer, ACPI_TRACE_LAYER_DEFAULT); ...@@ -263,6 +263,15 @@ ACPI_INIT_GLOBAL(u32, acpi_gbl_trace_dbg_layer, ACPI_TRACE_LAYER_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT); ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0); 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 * Other miscellaneous globals
*/ */
...@@ -366,6 +375,29 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); ...@@ -366,6 +375,29 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);
#endif /* ACPI_APPLICATION */ #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 * ACPICA public interface prototypes
...@@ -929,6 +961,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status ...@@ -929,6 +961,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
void **data, void **data,
void (*callback)(void *))) void (*callback)(void *)))
void acpi_run_debugger(char *batch_buffer);
void acpi_set_debugger_thread_id(acpi_thread_id thread_id); void acpi_set_debugger_thread_id(acpi_thread_id thread_id);
#endif /* __ACXFACE_H__ */ #endif /* __ACXFACE_H__ */
...@@ -150,6 +150,8 @@ ...@@ -150,6 +150,8 @@
*/ */
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable #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
/* /*
* OSL interfaces used by utilities * OSL interfaces used by utilities
......
...@@ -129,6 +129,15 @@ static inline u8 acpi_os_readable(void *pointer, acpi_size length) ...@@ -129,6 +129,15 @@ static inline u8 acpi_os_readable(void *pointer, acpi_size length)
return TRUE; return TRUE;
} }
static inline acpi_status acpi_os_initialize_command_signals(void)
{
return AE_OK;
}
static inline void acpi_os_terminate_command_signals(void)
{
}
/* /*
* OSL interfaces added by Linux * OSL interfaces added by Linux
*/ */
......
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/dynamic_debug.h> #include <linux/dynamic_debug.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
...@@ -119,6 +121,75 @@ typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table); ...@@ -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, typedef int (*acpi_tbl_entry_handler)(struct acpi_subtable_header *header,
const unsigned long end); 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 #ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size); void acpi_initrd_override(void *data, size_t size);
#else #else
......
...@@ -10,18 +10,18 @@ ...@@ -10,18 +10,18 @@
include ../../scripts/Makefile.include include ../../scripts/Makefile.include
all: acpidump ec all: acpidbg acpidump ec
clean: acpidump_clean ec_clean clean: acpidbg_clean acpidump_clean ec_clean
install: acpidump_install ec_install install: acpidbg_install acpidump_install ec_install
uninstall: acpidump_uninstall ec_uninstall uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall
acpidump ec: FORCE acpidbg acpidump ec: FORCE
$(call descend,tools/$@,all) $(call descend,tools/$@,all)
acpidump_clean ec_clean: acpidbg_clean acpidump_clean ec_clean:
$(call descend,tools/$(@:_clean=),clean) $(call descend,tools/$(@:_clean=),clean)
acpidump_install ec_install: acpidbg_install acpidump_install ec_install:
$(call descend,tools/$(@:_install=),install) $(call descend,tools/$(@:_install=),install)
acpidump_uninstall ec_uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall:
$(call descend,tools/$(@:_uninstall=),uninstall) $(call descend,tools/$(@:_uninstall=),uninstall)
.PHONY: FORCE .PHONY: FORCE
# tools/power/acpi/tools/acpidbg/Makefile - ACPI tool Makefile
#
# Copyright (c) 2015, Intel Corporation
# Author: Lv Zheng <lv.zheng@intel.com>
#
# 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
/*
* ACPI AML interfacing userspace utility
*
* Copyright (C) 2015, Intel Corporation
* Authors: Lv Zheng <lv.zheng@intel.com>
*
* 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment