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