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