linux/drivers/acpi/custom_method.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * custom_method.c - debugfs interface for customizing ACPI control method
   4 */
   5
   6#include <linux/init.h>
   7#include <linux/module.h>
   8#include <linux/kernel.h>
   9#include <linux/uaccess.h>
  10#include <linux/debugfs.h>
  11#include <linux/acpi.h>
  12#include <linux/security.h>
  13
  14#include "internal.h"
  15
  16MODULE_LICENSE("GPL");
  17
  18static struct dentry *cm_dentry;
  19
  20/* /sys/kernel/debug/acpi/custom_method */
  21
  22static ssize_t cm_write(struct file *file, const char __user *user_buf,
  23                        size_t count, loff_t *ppos)
  24{
  25        static char *buf;
  26        static u32 max_size;
  27        static u32 uncopied_bytes;
  28
  29        struct acpi_table_header table;
  30        acpi_status status;
  31        int ret;
  32
  33        ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
  34        if (ret)
  35                return ret;
  36
  37        if (!(*ppos)) {
  38                /* parse the table header to get the table length */
  39                if (count <= sizeof(struct acpi_table_header))
  40                        return -EINVAL;
  41                if (copy_from_user(&table, user_buf,
  42                                   sizeof(struct acpi_table_header)))
  43                        return -EFAULT;
  44                uncopied_bytes = max_size = table.length;
  45                /* make sure the buf is not allocated */
  46                kfree(buf);
  47                buf = kzalloc(max_size, GFP_KERNEL);
  48                if (!buf)
  49                        return -ENOMEM;
  50        }
  51
  52        if (buf == NULL)
  53                return -EINVAL;
  54
  55        if ((*ppos > max_size) ||
  56            (*ppos + count > max_size) ||
  57            (*ppos + count < count) ||
  58            (count > uncopied_bytes)) {
  59                kfree(buf);
  60                buf = NULL;
  61                return -EINVAL;
  62        }
  63
  64        if (copy_from_user(buf + (*ppos), user_buf, count)) {
  65                kfree(buf);
  66                buf = NULL;
  67                return -EFAULT;
  68        }
  69
  70        uncopied_bytes -= count;
  71        *ppos += count;
  72
  73        if (!uncopied_bytes) {
  74                status = acpi_install_method(buf);
  75                kfree(buf);
  76                buf = NULL;
  77                if (ACPI_FAILURE(status))
  78                        return -EINVAL;
  79                add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
  80        }
  81
  82        return count;
  83}
  84
  85static const struct file_operations cm_fops = {
  86        .write = cm_write,
  87        .llseek = default_llseek,
  88};
  89
  90static int __init acpi_custom_method_init(void)
  91{
  92        cm_dentry = debugfs_create_file("custom_method", S_IWUSR,
  93                                        acpi_debugfs_dir, NULL, &cm_fops);
  94        return 0;
  95}
  96
  97static void __exit acpi_custom_method_exit(void)
  98{
  99        debugfs_remove(cm_dentry);
 100}
 101
 102module_init(acpi_custom_method_init);
 103module_exit(acpi_custom_method_exit);
 104