1 /* 2 * Linux/SPARC PROM Configuration Driver 3 * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) 4 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) 5 * 6 * This character device driver allows user programs to access the 7 * PROM device tree. It is compatible with the SunOS /dev/openprom 8 * driver and the NetBSD /dev/openprom driver. The SunOS eeprom 9 * utility works without any modifications. 10 * 11 * The driver uses a minor number under the misc device major. The 12 * file read/write mode determines the type of access to the PROM. 13 * Interrupts are disabled whenever the driver calls into the PROM for 14 * sanity's sake. 15 */ 16 17 /* This program is free software; you can redistribute it and/or 18 * modify it under the terms of the GNU General Public License as 19 * published by the Free Software Foundation; either version 2 of the 20 * License, or (at your option) any later version. 21 * 22 * This program is distributed in the hope that it will be useful, but 23 * WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 * General Public License for more details. 26 * 27 * You should have received a copy of the GNU General Public License 28 * along with this program; if not, write to the Free Software 29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 30 */ 31 32 #define PROMLIB_INTERNAL 33 34 #include <linux/config.h> 35 #include <linux/module.h> 36 #include <linux/kernel.h> 37 #include <linux/sched.h> 38 #include <linux/errno.h> 39 #include <linux/slab.h> 40 #include <linux/string.h> 41 #include <linux/miscdevice.h> 42 #include <linux/init.h> 43 #include <linux/fs.h> 44 #include <asm/oplib.h> 45 #include <asm/system.h> 46 #include <asm/uaccess.h> 47 #include <asm/openpromio.h> 48 #ifdef CONFIG_PCI 49 #include <linux/pci.h> 50 #include <asm/pbm.h> 51 #endif 52 53 /* Private data kept by the driver for each descriptor. */ 54 typedef struct openprom_private_data 55 { 56 int current_node; /* Current node for SunOS ioctls. */ 57 int lastnode; /* Last valid node used by BSD ioctls. */ 58 } DATA; 59 60 /* ID of the PROM node containing all of the EEPROM options. */ 61 static int options_node = 0; 62 63 /* 64 * Copy an openpromio structure into kernel space from user space. 65 * This routine does error checking to make sure that all memory 66 * accesses are within bounds. A pointer to the allocated openpromio 67 * structure will be placed in "*opp_p". Return value is the length 68 * of the user supplied buffer. 69 */ 70 static int copyin(struct openpromio __user *info, struct openpromio **opp_p) 71 { 72 unsigned int bufsize; 73 74 if (!info || !opp_p) 75 return -EFAULT; 76 77 if (get_user(bufsize, &info->oprom_size)) 78 return -EFAULT; 79 80 if (bufsize == 0) 81 return -EINVAL; 82 83 /* If the bufsize is too large, just limit it. 84 * Fix from Jason Rappleye. 85 */ 86 if (bufsize > OPROMMAXPARAM) 87 bufsize = OPROMMAXPARAM; 88 89 if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) 90 return -ENOMEM; 91 memset(*opp_p, 0, sizeof(int) + bufsize + 1); 92 93 if (copy_from_user(&(*opp_p)->oprom_array, 94 &info->oprom_array, bufsize)) { 95 kfree(*opp_p); 96 return -EFAULT; 97 } 98 return bufsize; 99 } 100 101 static int getstrings(struct openpromio __user *info, struct openpromio **opp_p) 102 { 103 int n, bufsize; 104 char c; 105 106 if (!info || !opp_p) 107 return -EFAULT; 108 109 if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) 110 return -ENOMEM; 111 112 memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1); 113 (*opp_p)->oprom_size = 0; 114 115 n = bufsize = 0; 116 while ((n < 2) && (bufsize < OPROMMAXPARAM)) { 117 if (get_user(c, &info->oprom_array[bufsize])) { 118 kfree(*opp_p); 119 return -EFAULT; 120 } 121 if (c == '\0') 122 n++; 123 (*opp_p)->oprom_array[bufsize++] = c; 124 } 125 if (!n) { 126 kfree(*opp_p); 127 return -EINVAL; 128 } 129 return bufsize; 130 } 131 132 /* 133 * Copy an openpromio structure in kernel space back to user space. 134 */ 135 static int copyout(void __user *info, struct openpromio *opp, int len) 136 { 137 if (copy_to_user(info, opp, len)) 138 return -EFAULT; 139 return 0; 140 } 141 142 /* 143 * SunOS and Solaris /dev/openprom ioctl calls. 144 */ 145 static int openprom_sunos_ioctl(struct inode * inode, struct file * file, 146 unsigned int cmd, unsigned long arg, int node) 147 { 148 DATA *data = (DATA *) file->private_data; 149 char buffer[OPROMMAXPARAM+1], *buf; 150 struct openpromio *opp; 151 int bufsize, len, error = 0; 152 static int cnt; 153 void __user *argp = (void __user *)arg; 154 155 if (cmd == OPROMSETOPT) 156 bufsize = getstrings(argp, &opp); 157 else 158 bufsize = copyin(argp, &opp); 159 160 if (bufsize < 0) 161 return bufsize; 162 163 switch (cmd) { 164 case OPROMGETOPT: 165 case OPROMGETPROP: 166 len = prom_getproplen(node, opp->oprom_array); 167 168 if (len <= 0 || len > bufsize) { 169 error = copyout(argp, opp, sizeof(int)); 170 break; 171 } 172 173 len = prom_getproperty(node, opp->oprom_array, buffer, bufsize); 174 175 memcpy(opp->oprom_array, buffer, len); 176 opp->oprom_array[len] = '\0'; 177 opp->oprom_size = len; 178 179 error = copyout(argp, opp, sizeof(int) + bufsize); 180 break; 181 182 case OPROMNXTOPT: 183 case OPROMNXTPROP: 184 buf = prom_nextprop(node, opp->oprom_array, buffer); 185 186 len = strlen(buf); 187 if (len == 0 || len + 1 > bufsize) { 188 error = copyout(argp, opp, sizeof(int)); 189 break; 190 } 191 192 memcpy(opp->oprom_array, buf, len); 193 opp->oprom_array[len] = '\0'; 194 opp->oprom_size = ++len; 195 196 error = copyout(argp, opp, sizeof(int) + bufsize); 197 break; 198 199 case OPROMSETOPT: 200 case OPROMSETOPT2: 201 buf = opp->oprom_array + strlen(opp->oprom_array) + 1; 202 len = opp->oprom_array + bufsize - buf; 203 204 error = prom_setprop(options_node, opp->oprom_array, 205 buf, len); 206 207 if (error < 0) 208 error = -EINVAL; 209 break; 210 211 case OPROMNEXT: 212 case OPROMCHILD: 213 case OPROMSETCUR: 214 if (bufsize < sizeof(int)) { 215 error = -EINVAL; 216 break; 217 } 218 219 node = *((int *) opp->oprom_array); 220 221 switch (cmd) { 222 case OPROMNEXT: node = __prom_getsibling(node); break; 223 case OPROMCHILD: node = __prom_getchild(node); break; 224 case OPROMSETCUR: break; 225 } 226 227 data->current_node = node; 228 *((int *)opp->oprom_array) = node; 229 opp->oprom_size = sizeof(int); 230 231 error = copyout(argp, opp, bufsize + sizeof(int)); 232 break; 233 234 case OPROMPCI2NODE: 235 error = -EINVAL; 236 237 if (bufsize >= 2*sizeof(int)) { 238 #ifdef CONFIG_PCI 239 struct pci_dev *pdev; 240 struct pcidev_cookie *pcp; 241 pdev = pci_find_slot (((int *) opp->oprom_array)[0], 242 ((int *) opp->oprom_array)[1]); 243 244 pcp = pdev->sysdata; 245 if (pcp != NULL && pcp->prom_node != -1 && pcp->prom_node) { 246 node = pcp->prom_node; 247 data->current_node = node; 248 *((int *)opp->oprom_array) = node; 249 opp->oprom_size = sizeof(int); 250 error = copyout(argp, opp, bufsize + sizeof(int)); 251 } 252 #endif 253 } 254 break; 255 256 case OPROMPATH2NODE: 257 node = prom_finddevice(opp->oprom_array); 258 data->current_node = node; 259 *((int *)opp->oprom_array) = node; 260 opp->oprom_size = sizeof(int); 261 262 error = copyout(argp, opp, bufsize + sizeof(int)); 263 break; 264 265 case OPROMGETBOOTARGS: 266 buf = saved_command_line; 267 268 len = strlen(buf); 269 270 if (len > bufsize) { 271 error = -EINVAL; 272 break; 273 } 274 275 strcpy(opp->oprom_array, buf); 276 opp->oprom_size = len; 277 278 error = copyout(argp, opp, bufsize + sizeof(int)); 279 break; 280 281 case OPROMU2P: 282 case OPROMGETCONS: 283 case OPROMGETFBNAME: 284 if (cnt++ < 10) 285 printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n"); 286 error = -EINVAL; 287 break; 288 default: 289 if (cnt++ < 10) 290 printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); 291 error = -EINVAL; 292 break; 293 } 294 295 kfree(opp); 296 return error; 297 } 298 299 300 /* Return nonzero if a specific node is in the PROM device tree. */ 301 static int intree(int root, int node) 302 { 303 for (; root != 0; root = prom_getsibling(root)) 304 if (root == node || intree(prom_getchild(root),node)) 305 return 1; 306 return 0; 307 } 308 309 /* Return nonzero if a specific node is "valid". */ 310 static int goodnode(int n, DATA *data) 311 { 312 if (n == data->lastnode || n == prom_root_node || n == options_node) 313 return 1; 314 if (n == 0 || n == -1 || !intree(prom_root_node,n)) 315 return 0; 316 data->lastnode = n; 317 return 1; 318 } 319 320 /* Copy in a whole string from userspace into kernelspace. */ 321 static int copyin_string(char __user *user, size_t len, char **ptr) 322 { 323 char *tmp; 324 325 if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0) 326 return -EINVAL; 327 328 tmp = kmalloc(len + 1, GFP_KERNEL); 329 if (!tmp) 330 return -ENOMEM; 331 332 if(copy_from_user(tmp, user, len)) { 333 kfree(tmp); 334 return -EFAULT; 335 } 336 337 tmp[len] = '\0'; 338 339 *ptr = tmp; 340 341 return 0; 342 } 343 344 /* 345 * NetBSD /dev/openprom ioctl calls. 346 */ 347 static int openprom_bsd_ioctl(struct inode * inode, struct file * file, 348 unsigned int cmd, unsigned long arg) 349 { 350 DATA *data = (DATA *) file->private_data; 351 void __user *argp = (void __user *)arg; 352 struct opiocdesc op; 353 int error, node, len; 354 char *str, *tmp; 355 char buffer[64]; 356 static int cnt; 357 358 switch (cmd) { 359 case OPIOCGET: 360 if (copy_from_user(&op, argp, sizeof(op))) 361 return -EFAULT; 362 363 if (!goodnode(op.op_nodeid,data)) 364 return -EINVAL; 365 366 error = copyin_string(op.op_name, op.op_namelen, &str); 367 if (error) 368 return error; 369 370 len = prom_getproplen(op.op_nodeid,str); 371 372 if (len > op.op_buflen) { 373 kfree(str); 374 return -ENOMEM; 375 } 376 377 op.op_buflen = len; 378 379 if (len <= 0) { 380 kfree(str); 381 /* Verified by the above copy_from_user */ 382 if (__copy_to_user(argp, &op, 383 sizeof(op))) 384 return -EFAULT; 385 return 0; 386 } 387 388 tmp = kmalloc(len + 1, GFP_KERNEL); 389 if (!tmp) { 390 kfree(str); 391 return -ENOMEM; 392 } 393 394 prom_getproperty(op.op_nodeid, str, tmp, len); 395 396 tmp[len] = '\0'; 397 398 if (__copy_to_user(argp, &op, sizeof(op)) != 0 399 || copy_to_user(op.op_buf, tmp, len) != 0) 400 error = -EFAULT; 401 402 kfree(tmp); 403 kfree(str); 404 405 return error; 406 407 case OPIOCNEXTPROP: 408 if (copy_from_user(&op, argp, sizeof(op))) 409 return -EFAULT; 410 411 if (!goodnode(op.op_nodeid,data)) 412 return -EINVAL; 413 414 error = copyin_string(op.op_name, op.op_namelen, &str); 415 if (error) 416 return error; 417 418 tmp = prom_nextprop(op.op_nodeid,str,buffer); 419 420 if (tmp) { 421 len = strlen(tmp); 422 if (len > op.op_buflen) 423 len = op.op_buflen; 424 else 425 op.op_buflen = len; 426 } else { 427 len = op.op_buflen = 0; 428 } 429 430 if (!access_ok(VERIFY_WRITE, argp, sizeof(op))) { 431 kfree(str); 432 return -EFAULT; 433 } 434 435 if (!access_ok(VERIFY_WRITE, op.op_buf, len)) { 436 kfree(str); 437 return -EFAULT; 438 } 439 440 error = __copy_to_user(argp, &op, sizeof(op)); 441 if (!error) error = __copy_to_user(op.op_buf, tmp, len); 442 443 kfree(str); 444 445 return error; 446 447 case OPIOCSET: 448 if (copy_from_user(&op, argp, sizeof(op))) 449 return -EFAULT; 450 451 if (!goodnode(op.op_nodeid,data)) 452 return -EINVAL; 453 454 error = copyin_string(op.op_name, op.op_namelen, &str); 455 if (error) 456 return error; 457 458 error = copyin_string(op.op_buf, op.op_buflen, &tmp); 459 if (error) { 460 kfree(str); 461 return error; 462 } 463 464 len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1); 465 466 if (len != op.op_buflen) 467 return -EINVAL; 468 469 kfree(str); 470 kfree(tmp); 471 472 return 0; 473 474 case OPIOCGETOPTNODE: 475 if (copy_to_user(argp, &options_node, sizeof(int))) 476 return -EFAULT; 477 return 0; 478 479 case OPIOCGETNEXT: 480 case OPIOCGETCHILD: 481 if (copy_from_user(&node, argp, sizeof(int))) 482 return -EFAULT; 483 484 if (cmd == OPIOCGETNEXT) 485 node = __prom_getsibling(node); 486 else 487 node = __prom_getchild(node); 488 489 if (__copy_to_user(argp, &node, sizeof(int))) 490 return -EFAULT; 491 492 return 0; 493 494 default: 495 if (cnt++ < 10) 496 printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd); 497 return -EINVAL; 498 499 } 500 } 501 502 503 /* 504 * Handoff control to the correct ioctl handler. 505 */ 506 static int openprom_ioctl(struct inode * inode, struct file * file, 507 unsigned int cmd, unsigned long arg) 508 { 509 DATA *data = (DATA *) file->private_data; 510 static int cnt; 511 512 switch (cmd) { 513 case OPROMGETOPT: 514 case OPROMNXTOPT: 515 if ((file->f_mode & FMODE_READ) == 0) 516 return -EPERM; 517 return openprom_sunos_ioctl(inode, file, cmd, arg, 518 options_node); 519 520 case OPROMSETOPT: 521 case OPROMSETOPT2: 522 if ((file->f_mode & FMODE_WRITE) == 0) 523 return -EPERM; 524 return openprom_sunos_ioctl(inode, file, cmd, arg, 525 options_node); 526 527 case OPROMNEXT: 528 case OPROMCHILD: 529 case OPROMGETPROP: 530 case OPROMNXTPROP: 531 if ((file->f_mode & FMODE_READ) == 0) 532 return -EPERM; 533 return openprom_sunos_ioctl(inode, file, cmd, arg, 534 data->current_node); 535 536 case OPROMU2P: 537 case OPROMGETCONS: 538 case OPROMGETFBNAME: 539 case OPROMGETBOOTARGS: 540 case OPROMSETCUR: 541 case OPROMPCI2NODE: 542 case OPROMPATH2NODE: 543 if ((file->f_mode & FMODE_READ) == 0) 544 return -EPERM; 545 return openprom_sunos_ioctl(inode, file, cmd, arg, 0); 546 547 case OPIOCGET: 548 case OPIOCNEXTPROP: 549 case OPIOCGETOPTNODE: 550 case OPIOCGETNEXT: 551 case OPIOCGETCHILD: 552 if ((file->f_mode & FMODE_READ) == 0) 553 return -EBADF; 554 return openprom_bsd_ioctl(inode,file,cmd,arg); 555 556 case OPIOCSET: 557 if ((file->f_mode & FMODE_WRITE) == 0) 558 return -EBADF; 559 return openprom_bsd_ioctl(inode,file,cmd,arg); 560 561 default: 562 if (cnt++ < 10) 563 printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); 564 return -EINVAL; 565 } 566 } 567 568 static int openprom_open(struct inode * inode, struct file * file) 569 { 570 DATA *data; 571 572 data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL); 573 if (!data) 574 return -ENOMEM; 575 576 data->current_node = prom_root_node; 577 data->lastnode = prom_root_node; 578 file->private_data = (void *)data; 579 580 return 0; 581 } 582 583 static int openprom_release(struct inode * inode, struct file * file) 584 { 585 kfree(file->private_data); 586 return 0; 587 } 588 589 static struct file_operations openprom_fops = { 590 .owner = THIS_MODULE, 591 .llseek = no_llseek, 592 .ioctl = openprom_ioctl, 593 .open = openprom_open, 594 .release = openprom_release, 595 }; 596 597 static struct miscdevice openprom_dev = { 598 SUN_OPENPROM_MINOR, "openprom", &openprom_fops 599 }; 600 601 static int __init openprom_init(void) 602 { 603 int error; 604 605 error = misc_register(&openprom_dev); 606 if (error) { 607 printk(KERN_ERR "openprom: unable to get misc minor\n"); 608 return error; 609 } 610 611 options_node = prom_getchild(prom_root_node); 612 options_node = prom_searchsiblings(options_node,"options"); 613 614 if (options_node == 0 || options_node == -1) { 615 printk(KERN_ERR "openprom: unable to find options node\n"); 616 misc_deregister(&openprom_dev); 617 return -EIO; 618 } 619 620 return 0; 621 } 622 623 static void __exit openprom_cleanup(void) 624 { 625 misc_deregister(&openprom_dev); 626 } 627 628 module_init(openprom_init); 629 module_exit(openprom_cleanup); 630 MODULE_LICENSE("GPL"); 631