1526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger/*
25baa1be1cf4d118002503198bd3843e105868f65Zhang Rui * custom_method.c - debugfs interface for customizing ACPI control method
3526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger */
4526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
5526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger#include <linux/init.h>
6526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger#include <linux/module.h>
7526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger#include <linux/kernel.h>
8526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger#include <linux/uaccess.h>
9526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger#include <linux/debugfs.h>
108b48463f89429af408ff695244dc627e1acff4f7Lv Zheng#include <linux/acpi.h>
11526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
12526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger#include "internal.h"
13526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
14526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger#define _COMPONENT		ACPI_SYSTEM_COMPONENT
15526b4af47f44148c9d665e57723ed9f86634c6e3Thomas RenningerACPI_MODULE_NAME("custom_method");
16526b4af47f44148c9d665e57723ed9f86634c6e3Thomas RenningerMODULE_LICENSE("GPL");
17526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
18526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renningerstatic struct dentry *cm_dentry;
19526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
20526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger/* /sys/kernel/debug/acpi/custom_method */
21526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
22526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renningerstatic ssize_t cm_write(struct file *file, const char __user * user_buf,
23526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger			size_t count, loff_t *ppos)
24526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger{
25526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	static char *buf;
26526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	static u32 max_size;
27526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	static u32 uncopied_bytes;
28526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
29526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	struct acpi_table_header table;
30526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	acpi_status status;
31526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
32526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if (!(*ppos)) {
33526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		/* parse the table header to get the table length */
34526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		if (count <= sizeof(struct acpi_table_header))
35526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger			return -EINVAL;
36526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		if (copy_from_user(&table, user_buf,
37526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger				   sizeof(struct acpi_table_header)))
38526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger			return -EFAULT;
39526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		uncopied_bytes = max_size = table.length;
40526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		buf = kzalloc(max_size, GFP_KERNEL);
41526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		if (!buf)
42526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger			return -ENOMEM;
43526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	}
44526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
45526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if (buf == NULL)
46526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		return -EINVAL;
47526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
48526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if ((*ppos > max_size) ||
49526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	    (*ppos + count > max_size) ||
50526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	    (*ppos + count < count) ||
51526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	    (count > uncopied_bytes))
52526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		return -EINVAL;
53526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
54526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if (copy_from_user(buf + (*ppos), user_buf, count)) {
55526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		kfree(buf);
56526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		buf = NULL;
57526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		return -EFAULT;
58526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	}
59526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
60526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	uncopied_bytes -= count;
61526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	*ppos += count;
62526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
63526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if (!uncopied_bytes) {
64526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		status = acpi_install_method(buf);
65526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		kfree(buf);
66526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		buf = NULL;
67526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		if (ACPI_FAILURE(status))
68526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger			return -EINVAL;
69373d4d099761cb1f637bed488ab3871945882273Rusty Russell		add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
70526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	}
71526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
72526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	return count;
73526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger}
74526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
75526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renningerstatic const struct file_operations cm_fops = {
76526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	.write = cm_write,
77526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	.llseek = default_llseek,
78526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger};
79526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
80526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renningerstatic int __init acpi_custom_method_init(void)
81526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger{
82526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if (acpi_debugfs_dir == NULL)
83526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		return -ENOENT;
84526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
85526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	cm_dentry = debugfs_create_file("custom_method", S_IWUSR,
86526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger					acpi_debugfs_dir, NULL, &cm_fops);
87526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if (cm_dentry == NULL)
88526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		return -ENODEV;
89526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
90526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	return 0;
91526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger}
92526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
93526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renningerstatic void __exit acpi_custom_method_exit(void)
94526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger{
95526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger	if (cm_dentry)
96526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger		debugfs_remove(cm_dentry);
97526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger }
98526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renninger
99526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renningermodule_init(acpi_custom_method_init);
100526b4af47f44148c9d665e57723ed9f86634c6e3Thomas Renningermodule_exit(acpi_custom_method_exit);
101