| // 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); |