proc.c (6832B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * /proc/bus/pnp interface for Plug and Play devices 4 * 5 * Written by David Hinds, dahinds@users.sourceforge.net 6 * Modified by Thomas Hood 7 * 8 * The .../devices and .../<node> and .../boot/<node> files are 9 * utilized by the lspnp and setpnp utilities, supplied with the 10 * pcmcia-cs package. 11 * http://pcmcia-cs.sourceforge.net 12 * 13 * The .../escd file is utilized by the lsescd utility written by 14 * Gunther Mayer. 15 * 16 * The .../legacy_device_resources file is not used yet. 17 * 18 * The other files are human-readable. 19 */ 20 21#include <linux/module.h> 22#include <linux/kernel.h> 23#include <linux/slab.h> 24#include <linux/types.h> 25#include <linux/proc_fs.h> 26#include <linux/pnp.h> 27#include <linux/seq_file.h> 28#include <linux/init.h> 29 30#include <linux/uaccess.h> 31 32#include "pnpbios.h" 33 34static struct proc_dir_entry *proc_pnp = NULL; 35static struct proc_dir_entry *proc_pnp_boot = NULL; 36 37static int pnpconfig_proc_show(struct seq_file *m, void *v) 38{ 39 struct pnp_isa_config_struc pnps; 40 41 if (pnp_bios_isapnp_config(&pnps)) 42 return -EIO; 43 seq_printf(m, "structure_revision %d\n" 44 "number_of_CSNs %d\n" 45 "ISA_read_data_port 0x%x\n", 46 pnps.revision, pnps.no_csns, pnps.isa_rd_data_port); 47 return 0; 48} 49 50static int escd_info_proc_show(struct seq_file *m, void *v) 51{ 52 struct escd_info_struc escd; 53 54 if (pnp_bios_escd_info(&escd)) 55 return -EIO; 56 seq_printf(m, "min_ESCD_write_size %d\n" 57 "ESCD_size %d\n" 58 "NVRAM_base 0x%x\n", 59 escd.min_escd_write_size, 60 escd.escd_size, escd.nv_storage_base); 61 return 0; 62} 63 64#define MAX_SANE_ESCD_SIZE (32*1024) 65static int escd_proc_show(struct seq_file *m, void *v) 66{ 67 struct escd_info_struc escd; 68 char *tmpbuf; 69 int escd_size; 70 71 if (pnp_bios_escd_info(&escd)) 72 return -EIO; 73 74 /* sanity check */ 75 if (escd.escd_size > MAX_SANE_ESCD_SIZE) { 76 printk(KERN_ERR 77 "PnPBIOS: %s: ESCD size reported by BIOS escd_info call is too great\n", __func__); 78 return -EFBIG; 79 } 80 81 tmpbuf = kzalloc(escd.escd_size, GFP_KERNEL); 82 if (!tmpbuf) 83 return -ENOMEM; 84 85 if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) { 86 kfree(tmpbuf); 87 return -EIO; 88 } 89 90 escd_size = 91 (unsigned char)(tmpbuf[0]) + (unsigned char)(tmpbuf[1]) * 256; 92 93 /* sanity check */ 94 if (escd_size > MAX_SANE_ESCD_SIZE) { 95 printk(KERN_ERR "PnPBIOS: %s: ESCD size reported by" 96 " BIOS read_escd call is too great\n", __func__); 97 kfree(tmpbuf); 98 return -EFBIG; 99 } 100 101 seq_write(m, tmpbuf, escd_size); 102 kfree(tmpbuf); 103 return 0; 104} 105 106static int pnp_legacyres_proc_show(struct seq_file *m, void *v) 107{ 108 void *buf; 109 110 buf = kmalloc(65536, GFP_KERNEL); 111 if (!buf) 112 return -ENOMEM; 113 if (pnp_bios_get_stat_res(buf)) { 114 kfree(buf); 115 return -EIO; 116 } 117 118 seq_write(m, buf, 65536); 119 kfree(buf); 120 return 0; 121} 122 123static int pnp_devices_proc_show(struct seq_file *m, void *v) 124{ 125 struct pnp_bios_node *node; 126 u8 nodenum; 127 128 node = kzalloc(node_info.max_node_size, GFP_KERNEL); 129 if (!node) 130 return -ENOMEM; 131 132 for (nodenum = 0; nodenum < 0xff;) { 133 u8 thisnodenum = nodenum; 134 135 if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node)) 136 break; 137 seq_printf(m, "%02x\t%08x\t%3phC\t%04x\n", 138 node->handle, node->eisa_id, 139 node->type_code, node->flags); 140 if (nodenum <= thisnodenum) { 141 printk(KERN_ERR 142 "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", 143 "PnPBIOS: proc_read_devices:", 144 (unsigned int)nodenum, 145 (unsigned int)thisnodenum); 146 break; 147 } 148 } 149 kfree(node); 150 return 0; 151} 152 153static int pnpbios_proc_show(struct seq_file *m, void *v) 154{ 155 void *data = m->private; 156 struct pnp_bios_node *node; 157 int boot = (long)data >> 8; 158 u8 nodenum = (long)data; 159 int len; 160 161 node = kzalloc(node_info.max_node_size, GFP_KERNEL); 162 if (!node) 163 return -ENOMEM; 164 if (pnp_bios_get_dev_node(&nodenum, boot, node)) { 165 kfree(node); 166 return -EIO; 167 } 168 len = node->size - sizeof(struct pnp_bios_node); 169 seq_write(m, node->data, len); 170 kfree(node); 171 return 0; 172} 173 174static int pnpbios_proc_open(struct inode *inode, struct file *file) 175{ 176 return single_open(file, pnpbios_proc_show, pde_data(inode)); 177} 178 179static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf, 180 size_t count, loff_t *pos) 181{ 182 void *data = pde_data(file_inode(file)); 183 struct pnp_bios_node *node; 184 int boot = (long)data >> 8; 185 u8 nodenum = (long)data; 186 int ret = count; 187 188 node = kzalloc(node_info.max_node_size, GFP_KERNEL); 189 if (!node) 190 return -ENOMEM; 191 if (pnp_bios_get_dev_node(&nodenum, boot, node)) { 192 ret = -EIO; 193 goto out; 194 } 195 if (count != node->size - sizeof(struct pnp_bios_node)) { 196 ret = -EINVAL; 197 goto out; 198 } 199 if (copy_from_user(node->data, buf, count)) { 200 ret = -EFAULT; 201 goto out; 202 } 203 if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) { 204 ret = -EINVAL; 205 goto out; 206 } 207 ret = count; 208out: 209 kfree(node); 210 return ret; 211} 212 213static const struct proc_ops pnpbios_proc_ops = { 214 .proc_open = pnpbios_proc_open, 215 .proc_read = seq_read, 216 .proc_lseek = seq_lseek, 217 .proc_release = single_release, 218 .proc_write = pnpbios_proc_write, 219}; 220 221int pnpbios_interface_attach_device(struct pnp_bios_node *node) 222{ 223 char name[3]; 224 225 sprintf(name, "%02x", node->handle); 226 227 if (!proc_pnp) 228 return -EIO; 229 if (!pnpbios_dont_use_current_config) { 230 proc_create_data(name, 0644, proc_pnp, &pnpbios_proc_ops, 231 (void *)(long)(node->handle)); 232 } 233 234 if (!proc_pnp_boot) 235 return -EIO; 236 if (proc_create_data(name, 0644, proc_pnp_boot, &pnpbios_proc_ops, 237 (void *)(long)(node->handle + 0x100))) 238 return 0; 239 return -EIO; 240} 241 242/* 243 * When this is called, pnpbios functions are assumed to 244 * work and the pnpbios_dont_use_current_config flag 245 * should already have been set to the appropriate value 246 */ 247int __init pnpbios_proc_init(void) 248{ 249 proc_pnp = proc_mkdir("bus/pnp", NULL); 250 if (!proc_pnp) 251 return -EIO; 252 proc_pnp_boot = proc_mkdir("boot", proc_pnp); 253 if (!proc_pnp_boot) 254 return -EIO; 255 proc_create_single("devices", 0, proc_pnp, pnp_devices_proc_show); 256 proc_create_single("configuration_info", 0, proc_pnp, 257 pnpconfig_proc_show); 258 proc_create_single("escd_info", 0, proc_pnp, escd_info_proc_show); 259 proc_create_single("escd", S_IRUSR, proc_pnp, escd_proc_show); 260 proc_create_single("legacy_device_resources", 0, proc_pnp, 261 pnp_legacyres_proc_show); 262 return 0; 263} 264 265void __exit pnpbios_proc_exit(void) 266{ 267 int i; 268 char name[3]; 269 270 if (!proc_pnp) 271 return; 272 273 for (i = 0; i < 0xff; i++) { 274 sprintf(name, "%02x", i); 275 if (!pnpbios_dont_use_current_config) 276 remove_proc_entry(name, proc_pnp); 277 remove_proc_entry(name, proc_pnp_boot); 278 } 279 remove_proc_entry("legacy_device_resources", proc_pnp); 280 remove_proc_entry("escd", proc_pnp); 281 remove_proc_entry("escd_info", proc_pnp); 282 remove_proc_entry("configuration_info", proc_pnp); 283 remove_proc_entry("devices", proc_pnp); 284 remove_proc_entry("boot", proc_pnp); 285 remove_proc_entry("bus/pnp", NULL); 286}