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