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 34 static struct proc_dir_entry *proc_pnp = NULL; 35 static struct proc_dir_entry *proc_pnp_boot = NULL; 36 37 static 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 50 static 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) 65 static 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 106 static 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 123 static 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 153 static 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 174 static int pnpbios_proc_open(struct inode *inode, struct file *file) 175 { 176 return single_open(file, pnpbios_proc_show, PDE_DATA(inode)); 177 } 178 179 static 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; 208 out: 209 kfree(node); 210 return ret; 211 } 212 213 static const struct file_operations pnpbios_proc_fops = { 214 .owner = THIS_MODULE, 215 .open = pnpbios_proc_open, 216 .read = seq_read, 217 .llseek = seq_lseek, 218 .release = single_release, 219 .write = pnpbios_proc_write, 220 }; 221 222 int pnpbios_interface_attach_device(struct pnp_bios_node *node) 223 { 224 char name[3]; 225 226 sprintf(name, "%02x", node->handle); 227 228 if (!proc_pnp) 229 return -EIO; 230 if (!pnpbios_dont_use_current_config) { 231 proc_create_data(name, 0644, proc_pnp, &pnpbios_proc_fops, 232 (void *)(long)(node->handle)); 233 } 234 235 if (!proc_pnp_boot) 236 return -EIO; 237 if (proc_create_data(name, 0644, proc_pnp_boot, &pnpbios_proc_fops, 238 (void *)(long)(node->handle + 0x100))) 239 return 0; 240 return -EIO; 241 } 242 243 /* 244 * When this is called, pnpbios functions are assumed to 245 * work and the pnpbios_dont_use_current_config flag 246 * should already have been set to the appropriate value 247 */ 248 int __init pnpbios_proc_init(void) 249 { 250 proc_pnp = proc_mkdir("bus/pnp", NULL); 251 if (!proc_pnp) 252 return -EIO; 253 proc_pnp_boot = proc_mkdir("boot", proc_pnp); 254 if (!proc_pnp_boot) 255 return -EIO; 256 proc_create_single("devices", 0, proc_pnp, pnp_devices_proc_show); 257 proc_create_single("configuration_info", 0, proc_pnp, 258 pnpconfig_proc_show); 259 proc_create_single("escd_info", 0, proc_pnp, escd_info_proc_show); 260 proc_create_single("escd", S_IRUSR, proc_pnp, escd_proc_show); 261 proc_create_single("legacy_device_resources", 0, proc_pnp, 262 pnp_legacyres_proc_show); 263 return 0; 264 } 265 266 void __exit pnpbios_proc_exit(void) 267 { 268 int i; 269 char name[3]; 270 271 if (!proc_pnp) 272 return; 273 274 for (i = 0; i < 0xff; i++) { 275 sprintf(name, "%02x", i); 276 if (!pnpbios_dont_use_current_config) 277 remove_proc_entry(name, proc_pnp); 278 remove_proc_entry(name, proc_pnp_boot); 279 } 280 remove_proc_entry("legacy_device_resources", proc_pnp); 281 remove_proc_entry("escd", proc_pnp); 282 remove_proc_entry("escd_info", proc_pnp); 283 remove_proc_entry("configuration_info", proc_pnp); 284 remove_proc_entry("devices", proc_pnp); 285 remove_proc_entry("boot", proc_pnp); 286 remove_proc_entry("bus/pnp", NULL); 287 } 288