comedi_compat32.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*
  2. * comedi/comedi_compat32.c
  3. * 32-bit ioctl compatibility for 64-bit comedi kernel module.
  4. *
  5. * Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
  6. * Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
  7. *
  8. * COMEDI - Linux Control and Measurement Device Interface
  9. * Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
  10. *
  11. * This program is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. */
  21. #include <linux/uaccess.h>
  22. #include <linux/compat.h>
  23. #include <linux/fs.h>
  24. #include "comedi.h"
  25. #include "comedi_compat32.h"
  26. #define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
  27. #define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
  28. /*
  29. * N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
  30. * It's too late to change it now, but it only affects the command number.
  31. */
  32. #define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
  33. /*
  34. * N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
  35. * It's too late to change it now, but it only affects the command number.
  36. */
  37. #define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
  38. #define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
  39. #define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
  40. struct comedi32_chaninfo_struct {
  41. unsigned int subdev;
  42. compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */
  43. compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
  44. compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
  45. unsigned int unused[4];
  46. };
  47. struct comedi32_rangeinfo_struct {
  48. unsigned int range_type;
  49. compat_uptr_t range_ptr; /* 32-bit 'void *' */
  50. };
  51. struct comedi32_cmd_struct {
  52. unsigned int subdev;
  53. unsigned int flags;
  54. unsigned int start_src;
  55. unsigned int start_arg;
  56. unsigned int scan_begin_src;
  57. unsigned int scan_begin_arg;
  58. unsigned int convert_src;
  59. unsigned int convert_arg;
  60. unsigned int scan_end_src;
  61. unsigned int scan_end_arg;
  62. unsigned int stop_src;
  63. unsigned int stop_arg;
  64. compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
  65. unsigned int chanlist_len;
  66. compat_uptr_t data; /* 32-bit 'short *' */
  67. unsigned int data_len;
  68. };
  69. struct comedi32_insn_struct {
  70. unsigned int insn;
  71. unsigned int n;
  72. compat_uptr_t data; /* 32-bit 'unsigned int *' */
  73. unsigned int subdev;
  74. unsigned int chanspec;
  75. unsigned int unused[3];
  76. };
  77. struct comedi32_insnlist_struct {
  78. unsigned int n_insns;
  79. compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
  80. };
  81. /* Handle translated ioctl. */
  82. static int translated_ioctl(struct file *file, unsigned int cmd,
  83. unsigned long arg)
  84. {
  85. if (file->f_op->unlocked_ioctl)
  86. return file->f_op->unlocked_ioctl(file, cmd, arg);
  87. return -ENOTTY;
  88. }
  89. /* Handle 32-bit COMEDI_CHANINFO ioctl. */
  90. static int compat_chaninfo(struct file *file, unsigned long arg)
  91. {
  92. struct comedi_chaninfo __user *chaninfo;
  93. struct comedi32_chaninfo_struct __user *chaninfo32;
  94. int err;
  95. union {
  96. unsigned int uint;
  97. compat_uptr_t uptr;
  98. } temp;
  99. chaninfo32 = compat_ptr(arg);
  100. chaninfo = compat_alloc_user_space(sizeof(*chaninfo));
  101. /* Copy chaninfo structure. Ignore unused members. */
  102. if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32)) ||
  103. !access_ok(VERIFY_WRITE, chaninfo, sizeof(*chaninfo)))
  104. return -EFAULT;
  105. err = 0;
  106. err |= __get_user(temp.uint, &chaninfo32->subdev);
  107. err |= __put_user(temp.uint, &chaninfo->subdev);
  108. err |= __get_user(temp.uptr, &chaninfo32->maxdata_list);
  109. err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list);
  110. err |= __get_user(temp.uptr, &chaninfo32->flaglist);
  111. err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist);
  112. err |= __get_user(temp.uptr, &chaninfo32->rangelist);
  113. err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist);
  114. if (err)
  115. return -EFAULT;
  116. return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo);
  117. }
  118. /* Handle 32-bit COMEDI_RANGEINFO ioctl. */
  119. static int compat_rangeinfo(struct file *file, unsigned long arg)
  120. {
  121. struct comedi_rangeinfo __user *rangeinfo;
  122. struct comedi32_rangeinfo_struct __user *rangeinfo32;
  123. int err;
  124. union {
  125. unsigned int uint;
  126. compat_uptr_t uptr;
  127. } temp;
  128. rangeinfo32 = compat_ptr(arg);
  129. rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo));
  130. /* Copy rangeinfo structure. */
  131. if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32)) ||
  132. !access_ok(VERIFY_WRITE, rangeinfo, sizeof(*rangeinfo)))
  133. return -EFAULT;
  134. err = 0;
  135. err |= __get_user(temp.uint, &rangeinfo32->range_type);
  136. err |= __put_user(temp.uint, &rangeinfo->range_type);
  137. err |= __get_user(temp.uptr, &rangeinfo32->range_ptr);
  138. err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr);
  139. if (err)
  140. return -EFAULT;
  141. return translated_ioctl(file, COMEDI_RANGEINFO,
  142. (unsigned long)rangeinfo);
  143. }
  144. /* Copy 32-bit cmd structure to native cmd structure. */
  145. static int get_compat_cmd(struct comedi_cmd __user *cmd,
  146. struct comedi32_cmd_struct __user *cmd32)
  147. {
  148. int err;
  149. union {
  150. unsigned int uint;
  151. compat_uptr_t uptr;
  152. } temp;
  153. /* Copy cmd structure. */
  154. if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32)) ||
  155. !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd)))
  156. return -EFAULT;
  157. err = 0;
  158. err |= __get_user(temp.uint, &cmd32->subdev);
  159. err |= __put_user(temp.uint, &cmd->subdev);
  160. err |= __get_user(temp.uint, &cmd32->flags);
  161. err |= __put_user(temp.uint, &cmd->flags);
  162. err |= __get_user(temp.uint, &cmd32->start_src);
  163. err |= __put_user(temp.uint, &cmd->start_src);
  164. err |= __get_user(temp.uint, &cmd32->start_arg);
  165. err |= __put_user(temp.uint, &cmd->start_arg);
  166. err |= __get_user(temp.uint, &cmd32->scan_begin_src);
  167. err |= __put_user(temp.uint, &cmd->scan_begin_src);
  168. err |= __get_user(temp.uint, &cmd32->scan_begin_arg);
  169. err |= __put_user(temp.uint, &cmd->scan_begin_arg);
  170. err |= __get_user(temp.uint, &cmd32->convert_src);
  171. err |= __put_user(temp.uint, &cmd->convert_src);
  172. err |= __get_user(temp.uint, &cmd32->convert_arg);
  173. err |= __put_user(temp.uint, &cmd->convert_arg);
  174. err |= __get_user(temp.uint, &cmd32->scan_end_src);
  175. err |= __put_user(temp.uint, &cmd->scan_end_src);
  176. err |= __get_user(temp.uint, &cmd32->scan_end_arg);
  177. err |= __put_user(temp.uint, &cmd->scan_end_arg);
  178. err |= __get_user(temp.uint, &cmd32->stop_src);
  179. err |= __put_user(temp.uint, &cmd->stop_src);
  180. err |= __get_user(temp.uint, &cmd32->stop_arg);
  181. err |= __put_user(temp.uint, &cmd->stop_arg);
  182. err |= __get_user(temp.uptr, &cmd32->chanlist);
  183. err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr),
  184. &cmd->chanlist);
  185. err |= __get_user(temp.uint, &cmd32->chanlist_len);
  186. err |= __put_user(temp.uint, &cmd->chanlist_len);
  187. err |= __get_user(temp.uptr, &cmd32->data);
  188. err |= __put_user(compat_ptr(temp.uptr), &cmd->data);
  189. err |= __get_user(temp.uint, &cmd32->data_len);
  190. err |= __put_user(temp.uint, &cmd->data_len);
  191. return err ? -EFAULT : 0;
  192. }
  193. /* Copy native cmd structure to 32-bit cmd structure. */
  194. static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
  195. struct comedi_cmd __user *cmd)
  196. {
  197. int err;
  198. unsigned int temp;
  199. /*
  200. * Copy back most of cmd structure.
  201. *
  202. * Assume the pointer values are already valid.
  203. * (Could use ptr_to_compat() to set them.)
  204. */
  205. if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd)) ||
  206. !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32)))
  207. return -EFAULT;
  208. err = 0;
  209. err |= __get_user(temp, &cmd->subdev);
  210. err |= __put_user(temp, &cmd32->subdev);
  211. err |= __get_user(temp, &cmd->flags);
  212. err |= __put_user(temp, &cmd32->flags);
  213. err |= __get_user(temp, &cmd->start_src);
  214. err |= __put_user(temp, &cmd32->start_src);
  215. err |= __get_user(temp, &cmd->start_arg);
  216. err |= __put_user(temp, &cmd32->start_arg);
  217. err |= __get_user(temp, &cmd->scan_begin_src);
  218. err |= __put_user(temp, &cmd32->scan_begin_src);
  219. err |= __get_user(temp, &cmd->scan_begin_arg);
  220. err |= __put_user(temp, &cmd32->scan_begin_arg);
  221. err |= __get_user(temp, &cmd->convert_src);
  222. err |= __put_user(temp, &cmd32->convert_src);
  223. err |= __get_user(temp, &cmd->convert_arg);
  224. err |= __put_user(temp, &cmd32->convert_arg);
  225. err |= __get_user(temp, &cmd->scan_end_src);
  226. err |= __put_user(temp, &cmd32->scan_end_src);
  227. err |= __get_user(temp, &cmd->scan_end_arg);
  228. err |= __put_user(temp, &cmd32->scan_end_arg);
  229. err |= __get_user(temp, &cmd->stop_src);
  230. err |= __put_user(temp, &cmd32->stop_src);
  231. err |= __get_user(temp, &cmd->stop_arg);
  232. err |= __put_user(temp, &cmd32->stop_arg);
  233. /* Assume chanlist pointer is unchanged. */
  234. err |= __get_user(temp, &cmd->chanlist_len);
  235. err |= __put_user(temp, &cmd32->chanlist_len);
  236. /* Assume data pointer is unchanged. */
  237. err |= __get_user(temp, &cmd->data_len);
  238. err |= __put_user(temp, &cmd32->data_len);
  239. return err ? -EFAULT : 0;
  240. }
  241. /* Handle 32-bit COMEDI_CMD ioctl. */
  242. static int compat_cmd(struct file *file, unsigned long arg)
  243. {
  244. struct comedi_cmd __user *cmd;
  245. struct comedi32_cmd_struct __user *cmd32;
  246. int rc, err;
  247. cmd32 = compat_ptr(arg);
  248. cmd = compat_alloc_user_space(sizeof(*cmd));
  249. rc = get_compat_cmd(cmd, cmd32);
  250. if (rc)
  251. return rc;
  252. rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
  253. if (rc == -EAGAIN) {
  254. /* Special case: copy cmd back to user. */
  255. err = put_compat_cmd(cmd32, cmd);
  256. if (err)
  257. rc = err;
  258. }
  259. return rc;
  260. }
  261. /* Handle 32-bit COMEDI_CMDTEST ioctl. */
  262. static int compat_cmdtest(struct file *file, unsigned long arg)
  263. {
  264. struct comedi_cmd __user *cmd;
  265. struct comedi32_cmd_struct __user *cmd32;
  266. int rc, err;
  267. cmd32 = compat_ptr(arg);
  268. cmd = compat_alloc_user_space(sizeof(*cmd));
  269. rc = get_compat_cmd(cmd, cmd32);
  270. if (rc)
  271. return rc;
  272. rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd);
  273. if (rc < 0)
  274. return rc;
  275. err = put_compat_cmd(cmd32, cmd);
  276. if (err)
  277. rc = err;
  278. return rc;
  279. }
  280. /* Copy 32-bit insn structure to native insn structure. */
  281. static int get_compat_insn(struct comedi_insn __user *insn,
  282. struct comedi32_insn_struct __user *insn32)
  283. {
  284. int err;
  285. union {
  286. unsigned int uint;
  287. compat_uptr_t uptr;
  288. } temp;
  289. /* Copy insn structure. Ignore the unused members. */
  290. err = 0;
  291. if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32)) ||
  292. !access_ok(VERIFY_WRITE, insn, sizeof(*insn)))
  293. return -EFAULT;
  294. err |= __get_user(temp.uint, &insn32->insn);
  295. err |= __put_user(temp.uint, &insn->insn);
  296. err |= __get_user(temp.uint, &insn32->n);
  297. err |= __put_user(temp.uint, &insn->n);
  298. err |= __get_user(temp.uptr, &insn32->data);
  299. err |= __put_user(compat_ptr(temp.uptr), &insn->data);
  300. err |= __get_user(temp.uint, &insn32->subdev);
  301. err |= __put_user(temp.uint, &insn->subdev);
  302. err |= __get_user(temp.uint, &insn32->chanspec);
  303. err |= __put_user(temp.uint, &insn->chanspec);
  304. return err ? -EFAULT : 0;
  305. }
  306. /* Handle 32-bit COMEDI_INSNLIST ioctl. */
  307. static int compat_insnlist(struct file *file, unsigned long arg)
  308. {
  309. struct combined_insnlist {
  310. struct comedi_insnlist insnlist;
  311. struct comedi_insn insn[1];
  312. } __user *s;
  313. struct comedi32_insnlist_struct __user *insnlist32;
  314. struct comedi32_insn_struct __user *insn32;
  315. compat_uptr_t uptr;
  316. unsigned int n_insns, n;
  317. int err, rc;
  318. insnlist32 = compat_ptr(arg);
  319. /* Get 32-bit insnlist structure. */
  320. if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32)))
  321. return -EFAULT;
  322. err = 0;
  323. err |= __get_user(n_insns, &insnlist32->n_insns);
  324. err |= __get_user(uptr, &insnlist32->insns);
  325. insn32 = compat_ptr(uptr);
  326. if (err)
  327. return -EFAULT;
  328. /* Allocate user memory to copy insnlist and insns into. */
  329. s = compat_alloc_user_space(offsetof(struct combined_insnlist,
  330. insn[n_insns]));
  331. /* Set native insnlist structure. */
  332. if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist)))
  333. return -EFAULT;
  334. err |= __put_user(n_insns, &s->insnlist.n_insns);
  335. err |= __put_user(&s->insn[0], &s->insnlist.insns);
  336. if (err)
  337. return -EFAULT;
  338. /* Copy insn structures. */
  339. for (n = 0; n < n_insns; n++) {
  340. rc = get_compat_insn(&s->insn[n], &insn32[n]);
  341. if (rc)
  342. return rc;
  343. }
  344. return translated_ioctl(file, COMEDI_INSNLIST,
  345. (unsigned long)&s->insnlist);
  346. }
  347. /* Handle 32-bit COMEDI_INSN ioctl. */
  348. static int compat_insn(struct file *file, unsigned long arg)
  349. {
  350. struct comedi_insn __user *insn;
  351. struct comedi32_insn_struct __user *insn32;
  352. int rc;
  353. insn32 = compat_ptr(arg);
  354. insn = compat_alloc_user_space(sizeof(*insn));
  355. rc = get_compat_insn(insn, insn32);
  356. if (rc)
  357. return rc;
  358. return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
  359. }
  360. /*
  361. * compat_ioctl file operation.
  362. *
  363. * Returns -ENOIOCTLCMD for unrecognised ioctl codes.
  364. */
  365. long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  366. {
  367. int rc;
  368. switch (cmd) {
  369. case COMEDI_DEVCONFIG:
  370. case COMEDI_DEVINFO:
  371. case COMEDI_SUBDINFO:
  372. case COMEDI_BUFCONFIG:
  373. case COMEDI_BUFINFO:
  374. /* Just need to translate the pointer argument. */
  375. arg = (unsigned long)compat_ptr(arg);
  376. rc = translated_ioctl(file, cmd, arg);
  377. break;
  378. case COMEDI_LOCK:
  379. case COMEDI_UNLOCK:
  380. case COMEDI_CANCEL:
  381. case COMEDI_POLL:
  382. case COMEDI_SETRSUBD:
  383. case COMEDI_SETWSUBD:
  384. /* No translation needed. */
  385. rc = translated_ioctl(file, cmd, arg);
  386. break;
  387. case COMEDI32_CHANINFO:
  388. rc = compat_chaninfo(file, arg);
  389. break;
  390. case COMEDI32_RANGEINFO:
  391. rc = compat_rangeinfo(file, arg);
  392. break;
  393. case COMEDI32_CMD:
  394. rc = compat_cmd(file, arg);
  395. break;
  396. case COMEDI32_CMDTEST:
  397. rc = compat_cmdtest(file, arg);
  398. break;
  399. case COMEDI32_INSNLIST:
  400. rc = compat_insnlist(file, arg);
  401. break;
  402. case COMEDI32_INSN:
  403. rc = compat_insn(file, arg);
  404. break;
  405. default:
  406. rc = -ENOIOCTLCMD;
  407. break;
  408. }
  409. return rc;
  410. }