comedi_compat32.c revision f36624369ff2922f3aa8b5cef0376bcec759ebef
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 You should have received a copy of the GNU General Public License 22 along with this program; if not, write to the Free Software 23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24 25*/ 26 27#define __NO_VERSION__ 28#include "comedi.h" 29#include <linux/smp_lock.h> 30#include <linux/uaccess.h> 31 32#include "comedi_compat32.h" 33 34#ifdef CONFIG_COMPAT 35 36#ifndef HAVE_COMPAT_IOCTL 37#include <linux/ioctl32.h> /* for (un)register_ioctl32_conversion */ 38#endif 39 40#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct) 41#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct) 42/* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR. 43 * It's too late to change it now, but it only affects the command number. */ 44#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct) 45/* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR. 46 * It's too late to change it now, but it only affects the command number. */ 47#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct) 48#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct) 49#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct) 50 51struct comedi32_chaninfo_struct { 52 unsigned int subdev; 53 compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */ 54 compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */ 55 compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */ 56 unsigned int unused[4]; 57}; 58 59struct comedi32_rangeinfo_struct { 60 unsigned int range_type; 61 compat_uptr_t range_ptr; /* 32-bit 'void *' */ 62}; 63 64struct comedi32_cmd_struct { 65 unsigned int subdev; 66 unsigned int flags; 67 unsigned int start_src; 68 unsigned int start_arg; 69 unsigned int scan_begin_src; 70 unsigned int scan_begin_arg; 71 unsigned int convert_src; 72 unsigned int convert_arg; 73 unsigned int scan_end_src; 74 unsigned int scan_end_arg; 75 unsigned int stop_src; 76 unsigned int stop_arg; 77 compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */ 78 unsigned int chanlist_len; 79 compat_uptr_t data; /* 32-bit 'short *' */ 80 unsigned int data_len; 81}; 82 83struct comedi32_insn_struct { 84 unsigned int insn; 85 unsigned int n; 86 compat_uptr_t data; /* 32-bit 'unsigned int *' */ 87 unsigned int subdev; 88 unsigned int chanspec; 89 unsigned int unused[3]; 90}; 91 92struct comedi32_insnlist_struct { 93 unsigned int n_insns; 94 compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ 95}; 96 97/* Handle translated ioctl. */ 98static int translated_ioctl(struct file *file, unsigned int cmd, 99 unsigned long arg) 100{ 101 if (!file->f_op) 102 return -ENOTTY; 103 104#ifdef HAVE_UNLOCKED_IOCTL 105 if (file->f_op->unlocked_ioctl) { 106 int rc = (int)(*file->f_op->unlocked_ioctl) (file, cmd, arg); 107 if (rc == -ENOIOCTLCMD) 108 rc = -ENOTTY; 109 return rc; 110 } 111#endif 112 if (file->f_op->ioctl) { 113 int rc; 114 lock_kernel(); 115 rc = (*file->f_op->ioctl) (file->f_dentry->d_inode, 116 file, cmd, arg); 117 unlock_kernel(); 118 return rc; 119 } 120 return -ENOTTY; 121} 122 123/* Handle 32-bit COMEDI_CHANINFO ioctl. */ 124static int compat_chaninfo(struct file *file, unsigned long arg) 125{ 126 struct comedi_chaninfo __user *chaninfo; 127 struct comedi32_chaninfo_struct __user *chaninfo32; 128 int err; 129 union { 130 unsigned int uint; 131 compat_uptr_t uptr; 132 } temp; 133 134 chaninfo32 = compat_ptr(arg); 135 chaninfo = compat_alloc_user_space(sizeof(*chaninfo)); 136 137 /* Copy chaninfo structure. Ignore unused members. */ 138 if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32)) 139 || !access_ok(VERIFY_WRITE, chaninfo, sizeof(*chaninfo))) { 140 return -EFAULT; 141 } 142 err = 0; 143 err |= __get_user(temp.uint, &chaninfo32->subdev); 144 err |= __put_user(temp.uint, &chaninfo->subdev); 145 err |= __get_user(temp.uptr, &chaninfo32->maxdata_list); 146 err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list); 147 err |= __get_user(temp.uptr, &chaninfo32->flaglist); 148 err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist); 149 err |= __get_user(temp.uptr, &chaninfo32->rangelist); 150 err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist); 151 if (err) 152 return -EFAULT; 153 154 return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo); 155} 156 157/* Handle 32-bit COMEDI_RANGEINFO ioctl. */ 158static int compat_rangeinfo(struct file *file, unsigned long arg) 159{ 160 struct comedi_rangeinfo __user *rangeinfo; 161 struct comedi32_rangeinfo_struct __user *rangeinfo32; 162 int err; 163 union { 164 unsigned int uint; 165 compat_uptr_t uptr; 166 } temp; 167 168 rangeinfo32 = compat_ptr(arg); 169 rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo)); 170 171 /* Copy rangeinfo structure. */ 172 if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32)) 173 || !access_ok(VERIFY_WRITE, rangeinfo, sizeof(*rangeinfo))) { 174 return -EFAULT; 175 } 176 err = 0; 177 err |= __get_user(temp.uint, &rangeinfo32->range_type); 178 err |= __put_user(temp.uint, &rangeinfo->range_type); 179 err |= __get_user(temp.uptr, &rangeinfo32->range_ptr); 180 err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr); 181 if (err) 182 return -EFAULT; 183 184 return translated_ioctl(file, COMEDI_RANGEINFO, 185 (unsigned long)rangeinfo); 186} 187 188/* Copy 32-bit cmd structure to native cmd structure. */ 189static int get_compat_cmd(struct comedi_cmd __user *cmd, 190 struct comedi32_cmd_struct __user *cmd32) 191{ 192 int err; 193 union { 194 unsigned int uint; 195 compat_uptr_t uptr; 196 } temp; 197 198 /* Copy cmd structure. */ 199 if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32)) 200 || !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd))) { 201 return -EFAULT; 202 } 203 err = 0; 204 err |= __get_user(temp.uint, &cmd32->subdev); 205 err |= __put_user(temp.uint, &cmd->subdev); 206 err |= __get_user(temp.uint, &cmd32->flags); 207 err |= __put_user(temp.uint, &cmd->flags); 208 err |= __get_user(temp.uint, &cmd32->start_src); 209 err |= __put_user(temp.uint, &cmd->start_src); 210 err |= __get_user(temp.uint, &cmd32->start_arg); 211 err |= __put_user(temp.uint, &cmd->start_arg); 212 err |= __get_user(temp.uint, &cmd32->scan_begin_src); 213 err |= __put_user(temp.uint, &cmd->scan_begin_src); 214 err |= __get_user(temp.uint, &cmd32->scan_begin_arg); 215 err |= __put_user(temp.uint, &cmd->scan_begin_arg); 216 err |= __get_user(temp.uint, &cmd32->convert_src); 217 err |= __put_user(temp.uint, &cmd->convert_src); 218 err |= __get_user(temp.uint, &cmd32->convert_arg); 219 err |= __put_user(temp.uint, &cmd->convert_arg); 220 err |= __get_user(temp.uint, &cmd32->scan_end_src); 221 err |= __put_user(temp.uint, &cmd->scan_end_src); 222 err |= __get_user(temp.uint, &cmd32->scan_end_arg); 223 err |= __put_user(temp.uint, &cmd->scan_end_arg); 224 err |= __get_user(temp.uint, &cmd32->stop_src); 225 err |= __put_user(temp.uint, &cmd->stop_src); 226 err |= __get_user(temp.uint, &cmd32->stop_arg); 227 err |= __put_user(temp.uint, &cmd->stop_arg); 228 err |= __get_user(temp.uptr, &cmd32->chanlist); 229 err |= __put_user(compat_ptr(temp.uptr), &cmd->chanlist); 230 err |= __get_user(temp.uint, &cmd32->chanlist_len); 231 err |= __put_user(temp.uint, &cmd->chanlist_len); 232 err |= __get_user(temp.uptr, &cmd32->data); 233 err |= __put_user(compat_ptr(temp.uptr), &cmd->data); 234 err |= __get_user(temp.uint, &cmd32->data_len); 235 err |= __put_user(temp.uint, &cmd->data_len); 236 return err ? -EFAULT : 0; 237} 238 239/* Copy native cmd structure to 32-bit cmd structure. */ 240static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32, 241 struct comedi_cmd __user *cmd) 242{ 243 int err; 244 unsigned int temp; 245 246 /* Copy back most of cmd structure. */ 247 /* Assume the pointer values are already valid. */ 248 /* (Could use ptr_to_compat() to set them, but that wasn't implemented 249 * until kernel version 2.6.11.) */ 250 if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd)) 251 || !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32))) { 252 return -EFAULT; 253 } 254 err = 0; 255 err |= __get_user(temp, &cmd->subdev); 256 err |= __put_user(temp, &cmd32->subdev); 257 err |= __get_user(temp, &cmd->flags); 258 err |= __put_user(temp, &cmd32->flags); 259 err |= __get_user(temp, &cmd->start_src); 260 err |= __put_user(temp, &cmd32->start_src); 261 err |= __get_user(temp, &cmd->start_arg); 262 err |= __put_user(temp, &cmd32->start_arg); 263 err |= __get_user(temp, &cmd->scan_begin_src); 264 err |= __put_user(temp, &cmd32->scan_begin_src); 265 err |= __get_user(temp, &cmd->scan_begin_arg); 266 err |= __put_user(temp, &cmd32->scan_begin_arg); 267 err |= __get_user(temp, &cmd->convert_src); 268 err |= __put_user(temp, &cmd32->convert_src); 269 err |= __get_user(temp, &cmd->convert_arg); 270 err |= __put_user(temp, &cmd32->convert_arg); 271 err |= __get_user(temp, &cmd->scan_end_src); 272 err |= __put_user(temp, &cmd32->scan_end_src); 273 err |= __get_user(temp, &cmd->scan_end_arg); 274 err |= __put_user(temp, &cmd32->scan_end_arg); 275 err |= __get_user(temp, &cmd->stop_src); 276 err |= __put_user(temp, &cmd32->stop_src); 277 err |= __get_user(temp, &cmd->stop_arg); 278 err |= __put_user(temp, &cmd32->stop_arg); 279 /* Assume chanlist pointer is unchanged. */ 280 err |= __get_user(temp, &cmd->chanlist_len); 281 err |= __put_user(temp, &cmd32->chanlist_len); 282 /* Assume data pointer is unchanged. */ 283 err |= __get_user(temp, &cmd->data_len); 284 err |= __put_user(temp, &cmd32->data_len); 285 return err ? -EFAULT : 0; 286} 287 288/* Handle 32-bit COMEDI_CMD ioctl. */ 289static int compat_cmd(struct file *file, unsigned long arg) 290{ 291 struct comedi_cmd __user *cmd; 292 struct comedi32_cmd_struct __user *cmd32; 293 int rc; 294 295 cmd32 = compat_ptr(arg); 296 cmd = compat_alloc_user_space(sizeof(*cmd)); 297 298 rc = get_compat_cmd(cmd, cmd32); 299 if (rc) 300 return rc; 301 302 return translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd); 303} 304 305/* Handle 32-bit COMEDI_CMDTEST ioctl. */ 306static int compat_cmdtest(struct file *file, unsigned long arg) 307{ 308 struct comedi_cmd __user *cmd; 309 struct comedi32_cmd_struct __user *cmd32; 310 int rc, err; 311 312 cmd32 = compat_ptr(arg); 313 cmd = compat_alloc_user_space(sizeof(*cmd)); 314 315 rc = get_compat_cmd(cmd, cmd32); 316 if (rc) 317 return rc; 318 319 rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd); 320 if (rc < 0) 321 return rc; 322 323 err = put_compat_cmd(cmd32, cmd); 324 if (err) 325 rc = err; 326 327 return rc; 328} 329 330/* Copy 32-bit insn structure to native insn structure. */ 331static int get_compat_insn(struct comedi_insn __user *insn, 332 struct comedi32_insn_struct __user *insn32) 333{ 334 int err; 335 union { 336 unsigned int uint; 337 compat_uptr_t uptr; 338 } temp; 339 340 /* Copy insn structure. Ignore the unused members. */ 341 err = 0; 342 if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32)) 343 || !access_ok(VERIFY_WRITE, insn, sizeof(*insn))) 344 return -EFAULT; 345 346 err |= __get_user(temp.uint, &insn32->insn); 347 err |= __put_user(temp.uint, &insn->insn); 348 err |= __get_user(temp.uint, &insn32->n); 349 err |= __put_user(temp.uint, &insn->n); 350 err |= __get_user(temp.uptr, &insn32->data); 351 err |= __put_user(compat_ptr(temp.uptr), &insn->data); 352 err |= __get_user(temp.uint, &insn32->subdev); 353 err |= __put_user(temp.uint, &insn->subdev); 354 err |= __get_user(temp.uint, &insn32->chanspec); 355 err |= __put_user(temp.uint, &insn->chanspec); 356 return err ? -EFAULT : 0; 357} 358 359/* Handle 32-bit COMEDI_INSNLIST ioctl. */ 360static int compat_insnlist(struct file *file, unsigned long arg) 361{ 362 struct combined_insnlist { 363 struct comedi_insnlist insnlist; 364 struct comedi_insn insn[1]; 365 } __user *s; 366 struct comedi32_insnlist_struct __user *insnlist32; 367 struct comedi32_insn_struct __user *insn32; 368 compat_uptr_t uptr; 369 unsigned int n_insns, n; 370 int err, rc; 371 372 insnlist32 = compat_ptr(arg); 373 374 /* Get 32-bit insnlist structure. */ 375 if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32))) 376 return -EFAULT; 377 378 err = 0; 379 err |= __get_user(n_insns, &insnlist32->n_insns); 380 err |= __get_user(uptr, &insnlist32->insns); 381 insn32 = compat_ptr(uptr); 382 if (err) 383 return -EFAULT; 384 385 /* Allocate user memory to copy insnlist and insns into. */ 386 s = compat_alloc_user_space(offsetof(struct combined_insnlist, 387 insn[n_insns])); 388 389 /* Set native insnlist structure. */ 390 if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist))) 391 return -EFAULT; 392 393 err |= __put_user(n_insns, &s->insnlist.n_insns); 394 err |= __put_user(&s->insn[0], &s->insnlist.insns); 395 if (err) 396 return -EFAULT; 397 398 /* Copy insn structures. */ 399 for (n = 0; n < n_insns; n++) { 400 rc = get_compat_insn(&s->insn[n], &insn32[n]); 401 if (rc) 402 return rc; 403 } 404 405 return translated_ioctl(file, COMEDI_INSNLIST, 406 (unsigned long)&s->insnlist); 407} 408 409/* Handle 32-bit COMEDI_INSN ioctl. */ 410static int compat_insn(struct file *file, unsigned long arg) 411{ 412 struct comedi_insn __user *insn; 413 struct comedi32_insn_struct __user *insn32; 414 int rc; 415 416 insn32 = compat_ptr(arg); 417 insn = compat_alloc_user_space(sizeof(*insn)); 418 419 rc = get_compat_insn(insn, insn32); 420 if (rc) 421 return rc; 422 423 return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn); 424} 425 426/* Process untranslated ioctl. */ 427/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */ 428static inline int raw_ioctl(struct file *file, unsigned int cmd, 429 unsigned long arg) 430{ 431 int rc; 432 433 switch (cmd) { 434 case COMEDI_DEVCONFIG: 435 case COMEDI_DEVINFO: 436 case COMEDI_SUBDINFO: 437 case COMEDI_BUFCONFIG: 438 case COMEDI_BUFINFO: 439 /* Just need to translate the pointer argument. */ 440 arg = (unsigned long)compat_ptr(arg); 441 rc = translated_ioctl(file, cmd, arg); 442 break; 443 case COMEDI_LOCK: 444 case COMEDI_UNLOCK: 445 case COMEDI_CANCEL: 446 case COMEDI_POLL: 447 /* No translation needed. */ 448 rc = translated_ioctl(file, cmd, arg); 449 break; 450 case COMEDI32_CHANINFO: 451 rc = compat_chaninfo(file, arg); 452 break; 453 case COMEDI32_RANGEINFO: 454 rc = compat_rangeinfo(file, arg); 455 break; 456 case COMEDI32_CMD: 457 rc = compat_cmd(file, arg); 458 break; 459 case COMEDI32_CMDTEST: 460 rc = compat_cmdtest(file, arg); 461 break; 462 case COMEDI32_INSNLIST: 463 rc = compat_insnlist(file, arg); 464 break; 465 case COMEDI32_INSN: 466 rc = compat_insn(file, arg); 467 break; 468 default: 469 rc = -ENOIOCTLCMD; 470 break; 471 } 472 return rc; 473} 474 475#ifdef HAVE_COMPAT_IOCTL /* defined in <linux/fs.h> 2.6.11 onwards */ 476 477/* compat_ioctl file operation. */ 478/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */ 479long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 480{ 481 return raw_ioctl(file, cmd, arg); 482} 483 484#else /* HAVE_COMPAT_IOCTL */ 485 486/* 487 * Brain-dead ioctl compatibility for 2.6.10 and earlier. 488 * 489 * It's brain-dead because cmd numbers need to be unique system-wide! 490 * The comedi driver could end up attempting to execute ioctls for non-Comedi 491 * devices because it registered the system-wide cmd code first. Similarly, 492 * another driver could end up attempting to execute ioctls for a Comedi 493 * device because it registered the cmd code first. Chaos ensues. 494 */ 495 496/* Handler for all 32-bit ioctl codes registered by this driver. */ 497static int mapped_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg, 498 struct file *file) 499{ 500 int rc; 501 502 /* Make sure we are dealing with a Comedi device. */ 503 if (imajor(file->f_dentry->d_inode) != COMEDI_MAJOR) 504 return -ENOTTY; 505 506 rc = raw_ioctl(file, cmd, arg); 507 /* Do not return -ENOIOCTLCMD. */ 508 if (rc == -ENOIOCTLCMD) 509 rc = -ENOTTY; 510 511 return rc; 512} 513 514struct ioctl32_map { 515 unsigned int cmd; 516 int (*handler) (unsigned int, unsigned int, unsigned long, 517 struct file *); 518 int registered; 519}; 520 521static struct ioctl32_map comedi_ioctl32_map[] = { 522 {COMEDI_DEVCONFIG, mapped_ioctl, 0}, 523 {COMEDI_DEVINFO, mapped_ioctl, 0}, 524 {COMEDI_SUBDINFO, mapped_ioctl, 0}, 525 {COMEDI_BUFCONFIG, mapped_ioctl, 0}, 526 {COMEDI_BUFINFO, mapped_ioctl, 0}, 527 {COMEDI_LOCK, mapped_ioctl, 0}, 528 {COMEDI_UNLOCK, mapped_ioctl, 0}, 529 {COMEDI_CANCEL, mapped_ioctl, 0}, 530 {COMEDI_POLL, mapped_ioctl, 0}, 531 {COMEDI32_CHANINFO, mapped_ioctl, 0}, 532 {COMEDI32_RANGEINFO, mapped_ioctl, 0}, 533 {COMEDI32_CMD, mapped_ioctl, 0}, 534 {COMEDI32_CMDTEST, mapped_ioctl, 0}, 535 {COMEDI32_INSNLIST, mapped_ioctl, 0}, 536 {COMEDI32_INSN, mapped_ioctl, 0}, 537}; 538 539#define NUM_IOCTL32_MAPS ARRAY_SIZE(comedi_ioctl32_map) 540 541/* Register system-wide 32-bit ioctl handlers. */ 542void comedi_register_ioctl32(void) 543{ 544 int n, rc; 545 546 for (n = 0; n < NUM_IOCTL32_MAPS; n++) { 547 rc = register_ioctl32_conversion(comedi_ioctl32_map[n].cmd, 548 comedi_ioctl32_map[n].handler); 549 if (rc) { 550 printk(KERN_WARNING 551 "comedi: failed to register 32-bit " 552 "compatible ioctl handler for 0x%X - " 553 "expect bad things to happen!\n", 554 comedi_ioctl32_map[n].cmd); 555 } 556 comedi_ioctl32_map[n].registered = !rc; 557 } 558} 559 560/* Unregister system-wide 32-bit ioctl translations. */ 561void comedi_unregister_ioctl32(void) 562{ 563 int n, rc; 564 565 for (n = 0; n < NUM_IOCTL32_MAPS; n++) { 566 if (comedi_ioctl32_map[n].registered) { 567 rc = unregister_ioctl32_conversion(comedi_ioctl32_map 568 [n].cmd, 569 comedi_ioctl32_map 570 [n].handler); 571 if (rc) { 572 printk(KERN_ERR 573 "comedi: failed to unregister 32-bit " 574 "compatible ioctl handler for 0x%X - " 575 "expect kernel Oops!\n", 576 comedi_ioctl32_map[n].cmd); 577 } else { 578 comedi_ioctl32_map[n].registered = 0; 579 } 580 } 581 } 582} 583 584#endif /* HAVE_COMPAT_IOCTL */ 585 586#endif /* CONFIG_COMPAT */ 587