| // 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 file_operations srm_env_proc_fops = { |
| .owner = THIS_MODULE, |
| .open = srm_env_proc_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| .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_fops, (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_fops, (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); |