openprom.c 14 KB

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