openprom.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:14k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

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