1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * srm_env.c - Access to SRM environment |
4 | * variables through linux' procfs |
5 | * |
6 | * (C) 2001,2002,2006 by Jan-Benedict Glaw <jbglaw@lug-owl.de> |
7 | * |
8 | * This driver is a modified version of Erik Mouw's example proc |
9 | * interface, so: thank you, Erik! He can be reached via email at |
10 | * <J.A.K.Mouw@its.tudelft.nl>. It is based on an idea |
11 | * provided by DEC^WCompaq^WIntel's "Jumpstart" CD. They |
12 | * included a patch like this as well. Thanks for idea! |
13 | */ |
14 | |
15 | #include <linux/kernel.h> |
16 | #include <linux/gfp.h> |
17 | #include <linux/module.h> |
18 | #include <linux/init.h> |
19 | #include <linux/proc_fs.h> |
20 | #include <linux/seq_file.h> |
21 | #include <asm/console.h> |
22 | #include <linux/uaccess.h> |
23 | #include <asm/machvec.h> |
24 | |
25 | #define BASE_DIR "srm_environment" /* Subdir in /proc/ */ |
26 | #define NAMED_DIR "named_variables" /* Subdir for known variables */ |
27 | #define NUMBERED_DIR "numbered_variables" /* Subdir for all variables */ |
28 | #define VERSION "0.0.6" /* Module version */ |
29 | #define NAME "srm_env" /* Module name */ |
30 | |
31 | MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>" ); |
32 | MODULE_DESCRIPTION("Accessing Alpha SRM environment through procfs interface" ); |
33 | MODULE_LICENSE("GPL" ); |
34 | |
35 | typedef struct _srm_env { |
36 | char *name; |
37 | unsigned long id; |
38 | } srm_env_t; |
39 | |
40 | static struct proc_dir_entry *base_dir; |
41 | static struct proc_dir_entry *named_dir; |
42 | static struct proc_dir_entry *numbered_dir; |
43 | |
44 | static srm_env_t srm_named_entries[] = { |
45 | { "auto_action" , ENV_AUTO_ACTION }, |
46 | { "boot_dev" , ENV_BOOT_DEV }, |
47 | { "bootdef_dev" , ENV_BOOTDEF_DEV }, |
48 | { "booted_dev" , ENV_BOOTED_DEV }, |
49 | { "boot_file" , ENV_BOOT_FILE }, |
50 | { "booted_file" , ENV_BOOTED_FILE }, |
51 | { "boot_osflags" , ENV_BOOT_OSFLAGS }, |
52 | { "booted_osflags" , ENV_BOOTED_OSFLAGS }, |
53 | { "boot_reset" , ENV_BOOT_RESET }, |
54 | { "dump_dev" , ENV_DUMP_DEV }, |
55 | { "enable_audit" , ENV_ENABLE_AUDIT }, |
56 | { "license" , ENV_LICENSE }, |
57 | { "char_set" , ENV_CHAR_SET }, |
58 | { "language" , ENV_LANGUAGE }, |
59 | { "tty_dev" , ENV_TTY_DEV }, |
60 | { NULL, 0 }, |
61 | }; |
62 | |
63 | static int srm_env_proc_show(struct seq_file *m, void *v) |
64 | { |
65 | unsigned long ret; |
66 | unsigned long id = (unsigned long)m->private; |
67 | char *page; |
68 | |
69 | page = (char *)__get_free_page(GFP_USER); |
70 | if (!page) |
71 | return -ENOMEM; |
72 | |
73 | ret = callback_getenv(id, page, PAGE_SIZE); |
74 | |
75 | if ((ret >> 61) == 0) { |
76 | seq_write(seq: m, data: page, len: ret); |
77 | ret = 0; |
78 | } else |
79 | ret = -EFAULT; |
80 | free_page((unsigned long)page); |
81 | return ret; |
82 | } |
83 | |
84 | static int srm_env_proc_open(struct inode *inode, struct file *file) |
85 | { |
86 | return single_open(file, srm_env_proc_show, pde_data(inode)); |
87 | } |
88 | |
89 | static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer, |
90 | size_t count, loff_t *pos) |
91 | { |
92 | int res; |
93 | unsigned long id = (unsigned long)pde_data(inode: file_inode(f: file)); |
94 | char *buf = (char *) __get_free_page(GFP_USER); |
95 | unsigned long ret1, ret2; |
96 | |
97 | if (!buf) |
98 | return -ENOMEM; |
99 | |
100 | res = -EINVAL; |
101 | if (count >= PAGE_SIZE) |
102 | goto out; |
103 | |
104 | res = -EFAULT; |
105 | if (copy_from_user(to: buf, from: buffer, n: count)) |
106 | goto out; |
107 | buf[count] = '\0'; |
108 | |
109 | ret1 = callback_setenv(id, buf, count); |
110 | if ((ret1 >> 61) == 0) { |
111 | do |
112 | ret2 = callback_save_env(); |
113 | while((ret2 >> 61) == 1); |
114 | res = (int) ret1; |
115 | } |
116 | |
117 | out: |
118 | free_page((unsigned long)buf); |
119 | return res; |
120 | } |
121 | |
122 | static const struct proc_ops srm_env_proc_ops = { |
123 | .proc_open = srm_env_proc_open, |
124 | .proc_read = seq_read, |
125 | .proc_lseek = seq_lseek, |
126 | .proc_release = single_release, |
127 | .proc_write = srm_env_proc_write, |
128 | }; |
129 | |
130 | static int __init |
131 | srm_env_init(void) |
132 | { |
133 | srm_env_t *entry; |
134 | unsigned long var_num; |
135 | |
136 | /* |
137 | * Check system |
138 | */ |
139 | if (!alpha_using_srm) { |
140 | printk(KERN_INFO "%s: This Alpha system doesn't " |
141 | "know about SRM (or you've booted " |
142 | "SRM->MILO->Linux, which gets " |
143 | "misdetected)...\n" , __func__); |
144 | return -ENODEV; |
145 | } |
146 | |
147 | /* |
148 | * Create base directory |
149 | */ |
150 | base_dir = proc_mkdir(BASE_DIR, NULL); |
151 | if (!base_dir) { |
152 | printk(KERN_ERR "Couldn't create base dir /proc/%s\n" , |
153 | BASE_DIR); |
154 | return -ENOMEM; |
155 | } |
156 | |
157 | /* |
158 | * Create per-name subdirectory |
159 | */ |
160 | named_dir = proc_mkdir(NAMED_DIR, base_dir); |
161 | if (!named_dir) { |
162 | printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n" , |
163 | BASE_DIR, NAMED_DIR); |
164 | goto cleanup; |
165 | } |
166 | |
167 | /* |
168 | * Create per-number subdirectory |
169 | */ |
170 | numbered_dir = proc_mkdir(NUMBERED_DIR, base_dir); |
171 | if (!numbered_dir) { |
172 | printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n" , |
173 | BASE_DIR, NUMBERED_DIR); |
174 | goto cleanup; |
175 | |
176 | } |
177 | |
178 | /* |
179 | * Create all named nodes |
180 | */ |
181 | entry = srm_named_entries; |
182 | while (entry->name && entry->id) { |
183 | if (!proc_create_data(entry->name, 0644, named_dir, |
184 | &srm_env_proc_ops, (void *)entry->id)) |
185 | goto cleanup; |
186 | entry++; |
187 | } |
188 | |
189 | /* |
190 | * Create all numbered nodes |
191 | */ |
192 | for (var_num = 0; var_num <= 255; var_num++) { |
193 | char name[4]; |
194 | sprintf(buf: name, fmt: "%ld" , var_num); |
195 | if (!proc_create_data(name, 0644, numbered_dir, |
196 | &srm_env_proc_ops, (void *)var_num)) |
197 | goto cleanup; |
198 | } |
199 | |
200 | printk(KERN_INFO "%s: version %s loaded successfully\n" , NAME, |
201 | VERSION); |
202 | |
203 | return 0; |
204 | |
205 | cleanup: |
206 | remove_proc_subtree(BASE_DIR, NULL); |
207 | return -ENOMEM; |
208 | } |
209 | |
210 | static void __exit |
211 | srm_env_exit(void) |
212 | { |
213 | remove_proc_subtree(BASE_DIR, NULL); |
214 | printk(KERN_INFO "%s: unloaded successfully\n" , NAME); |
215 | } |
216 | |
217 | module_init(srm_env_init); |
218 | module_exit(srm_env_exit); |
219 | |