root/arch/alpha/kernel/srm_env.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * srm_env.c - Access to SRM environment
 *             variables through linux' procfs
 *
 * (C) 2001,2002,2006 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
 *
 * This driver is a modified version of Erik Mouw's example proc
 * interface, so: thank you, Erik! He can be reached via email at
 * <J.A.K.Mouw@its.tudelft.nl>. It is based on an idea
 * provided by DEC^WCompaq^WIntel's "Jumpstart" CD. They
 * included a patch like this as well. Thanks for idea!
 */

#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/console.h>
#include <linux/uaccess.h>
#include <asm/machvec.h>

#define BASE_DIR        "srm_environment"       /* Subdir in /proc/             */
#define NAMED_DIR       "named_variables"       /* Subdir for known variables   */
#define NUMBERED_DIR    "numbered_variables"    /* Subdir for all variables     */
#define VERSION         "0.0.6"                 /* Module version               */
#define NAME            "srm_env"               /* Module name                  */

MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
MODULE_DESCRIPTION("Accessing Alpha SRM environment through procfs interface");
MODULE_LICENSE("GPL");

typedef struct _srm_env {
        char                    *name;
        unsigned long           id;
} srm_env_t;

static struct proc_dir_entry    *base_dir;
static struct proc_dir_entry    *named_dir;
static struct proc_dir_entry    *numbered_dir;

static srm_env_t        srm_named_entries[] = {
        { "auto_action",        ENV_AUTO_ACTION         },
        { "boot_dev",           ENV_BOOT_DEV            },
        { "bootdef_dev",        ENV_BOOTDEF_DEV         },
        { "booted_dev",         ENV_BOOTED_DEV          },
        { "boot_file",          ENV_BOOT_FILE           },
        { "booted_file",        ENV_BOOTED_FILE         },
        { "boot_osflags",       ENV_BOOT_OSFLAGS        },
        { "booted_osflags",     ENV_BOOTED_OSFLAGS      },
        { "boot_reset",         ENV_BOOT_RESET          },
        { "dump_dev",           ENV_DUMP_DEV            },
        { "enable_audit",       ENV_ENABLE_AUDIT        },
        { "license",            ENV_LICENSE             },
        { "char_set",           ENV_CHAR_SET            },
        { "language",           ENV_LANGUAGE            },
        { "tty_dev",            ENV_TTY_DEV             },
        { NULL,                 0                       },
};

static int srm_env_proc_show(struct seq_file *m, void *v)
{
        unsigned long   ret;
        unsigned long   id = (unsigned long)m->private;
        char            *page;

        page = (char *)__get_free_page(GFP_USER);
        if (!page)
                return -ENOMEM;

        ret = callback_getenv(id, page, PAGE_SIZE);

        if ((ret >> 61) == 0) {
                seq_write(m, page, ret);
                ret = 0;
        } else
                ret = -EFAULT;
        free_page((unsigned long)page);
        return ret;
}

static int srm_env_proc_open(struct inode *inode, struct file *file)
{
        return single_open(file, srm_env_proc_show, pde_data(inode));
}

static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
                                  size_t count, loff_t *pos)
{
        int res;
        unsigned long   id = (unsigned long)pde_data(file_inode(file));
        char            *buf = (char *) __get_free_page(GFP_USER);
        unsigned long   ret1, ret2;

        if (!buf)
                return -ENOMEM;

        res = -EINVAL;
        if (count >= PAGE_SIZE)
                goto out;

        res = -EFAULT;
        if (copy_from_user(buf, buffer, count))
                goto out;
        buf[count] = '\0';

        ret1 = callback_setenv(id, buf, count);
        if ((ret1 >> 61) == 0) {
                do
                        ret2 = callback_save_env();
                while((ret2 >> 61) == 1);
                res = (int) ret1;
        }

 out:
        free_page((unsigned long)buf);
        return res;
}

static const struct proc_ops srm_env_proc_ops = {
        .proc_open      = srm_env_proc_open,
        .proc_read      = seq_read,
        .proc_lseek     = seq_lseek,
        .proc_release   = single_release,
        .proc_write     = srm_env_proc_write,
};

static int __init
srm_env_init(void)
{
        srm_env_t       *entry;
        unsigned long   var_num;

        /*
         * Check system
         */
        if (!alpha_using_srm) {
                printk(KERN_INFO "%s: This Alpha system doesn't "
                                "know about SRM (or you've booted "
                                "SRM->MILO->Linux, which gets "
                                "misdetected)...\n", __func__);
                return -ENODEV;
        }

        /*
         * Create base directory
         */
        base_dir = proc_mkdir(BASE_DIR, NULL);
        if (!base_dir) {
                printk(KERN_ERR "Couldn't create base dir /proc/%s\n",
                                BASE_DIR);
                return -ENOMEM;
        }

        /*
         * Create per-name subdirectory
         */
        named_dir = proc_mkdir(NAMED_DIR, base_dir);
        if (!named_dir) {
                printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n",
                                BASE_DIR, NAMED_DIR);
                goto cleanup;
        }

        /*
         * Create per-number subdirectory
         */
        numbered_dir = proc_mkdir(NUMBERED_DIR, base_dir);
        if (!numbered_dir) {
                printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n",
                                BASE_DIR, NUMBERED_DIR);
                goto cleanup;

        }

        /*
         * Create all named nodes
         */
        entry = srm_named_entries;
        while (entry->name && entry->id) {
                if (!proc_create_data(entry->name, 0644, named_dir,
                             &srm_env_proc_ops, (void *)entry->id))
                        goto cleanup;
                entry++;
        }

        /*
         * Create all numbered nodes
         */
        for (var_num = 0; var_num <= 255; var_num++) {
                char name[4];
                sprintf(name, "%ld", var_num);
                if (!proc_create_data(name, 0644, numbered_dir,
                             &srm_env_proc_ops, (void *)var_num))
                        goto cleanup;
        }

        printk(KERN_INFO "%s: version %s loaded successfully\n", NAME,
                        VERSION);

        return 0;

cleanup:
        remove_proc_subtree(BASE_DIR, NULL);
        return -ENOMEM;
}

static void __exit
srm_env_exit(void)
{
        remove_proc_subtree(BASE_DIR, NULL);
        printk(KERN_INFO "%s: unloaded successfully\n", NAME);
}

module_init(srm_env_init);
module_exit(srm_env_exit);