1219820Sjeff/*- 2219820Sjeff * Copyright (c) 2010 Isilon Systems, Inc. 3219820Sjeff * Copyright (c) 2010 iX Systems, Inc. 4219820Sjeff * Copyright (c) 2010 Panasas, Inc. 5219820Sjeff * All rights reserved. 6219820Sjeff * 7219820Sjeff * Redistribution and use in source and binary forms, with or without 8219820Sjeff * modification, are permitted provided that the following conditions 9219820Sjeff * are met: 10219820Sjeff * 1. Redistributions of source code must retain the above copyright 11219820Sjeff * notice unmodified, this list of conditions, and the following 12219820Sjeff * disclaimer. 13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 14219820Sjeff * notice, this list of conditions and the following disclaimer in the 15219820Sjeff * documentation and/or other materials provided with the distribution. 16219820Sjeff * 17219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27219820Sjeff */ 28219820Sjeff 29219820Sjeff#include <sys/param.h> 30219820Sjeff#include <sys/systm.h> 31219820Sjeff#include <sys/malloc.h> 32219820Sjeff#include <sys/kernel.h> 33219820Sjeff#include <sys/sysctl.h> 34219820Sjeff#include <sys/lock.h> 35219820Sjeff#include <sys/mutex.h> 36219820Sjeff#include <sys/bus.h> 37219820Sjeff#include <sys/fcntl.h> 38219820Sjeff#include <sys/file.h> 39219820Sjeff#include <sys/filio.h> 40219820Sjeff 41219820Sjeff#include <vm/vm.h> 42219820Sjeff#include <vm/pmap.h> 43219820Sjeff 44219820Sjeff#include <machine/stdarg.h> 45219820Sjeff#include <machine/pmap.h> 46219820Sjeff 47219820Sjeff#include <linux/kobject.h> 48219820Sjeff#include <linux/device.h> 49219820Sjeff#include <linux/slab.h> 50219820Sjeff#include <linux/module.h> 51219820Sjeff#include <linux/cdev.h> 52219820Sjeff#include <linux/file.h> 53219820Sjeff#include <linux/sysfs.h> 54219820Sjeff#include <linux/mm.h> 55219820Sjeff#include <linux/io.h> 56219820Sjeff#include <linux/vmalloc.h> 57219820Sjeff 58219820Sjeff#include <vm/vm_pager.h> 59219820Sjeff 60219820SjeffMALLOC_DEFINE(M_KMALLOC, "linux", "Linux kmalloc compat"); 61219820Sjeff 62219820Sjeff#include <linux/rbtree.h> 63219820Sjeff/* Undo Linux compat changes. */ 64219820Sjeff#undef RB_ROOT 65219820Sjeff#undef file 66219820Sjeff#undef cdev 67219820Sjeff#define RB_ROOT(head) (head)->rbh_root 68219820Sjeff#undef LIST_HEAD 69219820Sjeff/* From sys/queue.h */ 70219820Sjeff#define LIST_HEAD(name, type) \ 71219820Sjeffstruct name { \ 72219820Sjeff struct type *lh_first; /* first element */ \ 73219820Sjeff} 74219820Sjeff 75219820Sjeffstruct kobject class_root; 76219820Sjeffstruct device linux_rootdev; 77219820Sjeffstruct class miscclass; 78219820Sjeffstruct list_head pci_drivers; 79219820Sjeffstruct list_head pci_devices; 80219820Sjeffspinlock_t pci_lock; 81219820Sjeff 82219820Sjeffint 83219820Sjeffpanic_cmp(struct rb_node *one, struct rb_node *two) 84219820Sjeff{ 85219820Sjeff panic("no cmp"); 86219820Sjeff} 87219820Sjeff 88219820SjeffRB_GENERATE(linux_root, rb_node, __entry, panic_cmp); 89219820Sjeff 90219820Sjeffint 91219820Sjeffkobject_set_name(struct kobject *kobj, const char *fmt, ...) 92219820Sjeff{ 93219820Sjeff va_list args; 94219820Sjeff int error; 95219820Sjeff 96219820Sjeff va_start(args, fmt); 97219820Sjeff error = kobject_set_name_vargs(kobj, fmt, args); 98219820Sjeff va_end(args); 99219820Sjeff 100219820Sjeff return (error); 101219820Sjeff} 102219820Sjeff 103219820Sjeffstatic inline int 104219820Sjeffkobject_add_complete(struct kobject *kobj, struct kobject *parent) 105219820Sjeff{ 106219820Sjeff struct kobj_type *t; 107219820Sjeff int error; 108219820Sjeff 109219820Sjeff kobj->parent = kobject_get(parent); 110219820Sjeff error = sysfs_create_dir(kobj); 111219820Sjeff if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) { 112219820Sjeff struct attribute **attr; 113219820Sjeff t = kobj->ktype; 114219820Sjeff 115219820Sjeff for (attr = t->default_attrs; *attr != NULL; attr++) { 116219820Sjeff error = sysfs_create_file(kobj, *attr); 117219820Sjeff if (error) 118219820Sjeff break; 119219820Sjeff } 120219820Sjeff if (error) 121219820Sjeff sysfs_remove_dir(kobj); 122219820Sjeff 123219820Sjeff } 124219820Sjeff return (error); 125219820Sjeff} 126219820Sjeff 127219820Sjeffint 128219820Sjeffkobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) 129219820Sjeff{ 130219820Sjeff va_list args; 131219820Sjeff int error; 132219820Sjeff 133219820Sjeff va_start(args, fmt); 134219820Sjeff error = kobject_set_name_vargs(kobj, fmt, args); 135219820Sjeff va_end(args); 136219820Sjeff if (error) 137219820Sjeff return (error); 138219820Sjeff 139219820Sjeff return kobject_add_complete(kobj, parent); 140219820Sjeff} 141219820Sjeff 142219820Sjeffvoid 143219820Sjeffkobject_release(struct kref *kref) 144219820Sjeff{ 145219820Sjeff struct kobject *kobj; 146219820Sjeff char *name; 147219820Sjeff 148219820Sjeff kobj = container_of(kref, struct kobject, kref); 149219820Sjeff sysfs_remove_dir(kobj); 150219820Sjeff if (kobj->parent) 151219820Sjeff kobject_put(kobj->parent); 152219820Sjeff kobj->parent = NULL; 153219820Sjeff name = kobj->name; 154219820Sjeff if (kobj->ktype && kobj->ktype->release) 155219820Sjeff kobj->ktype->release(kobj); 156219820Sjeff kfree(name); 157219820Sjeff} 158219820Sjeff 159219820Sjeffstatic void 160219820Sjeffkobject_kfree(struct kobject *kobj) 161219820Sjeff{ 162219820Sjeff 163219820Sjeff kfree(kobj); 164219820Sjeff} 165219820Sjeff 166219820Sjeffstruct kobj_type kfree_type = { .release = kobject_kfree }; 167219820Sjeff 168219820Sjeffstruct device * 169219820Sjeffdevice_create(struct class *class, struct device *parent, dev_t devt, 170219820Sjeff void *drvdata, const char *fmt, ...) 171219820Sjeff{ 172219820Sjeff struct device *dev; 173219820Sjeff va_list args; 174219820Sjeff 175219820Sjeff dev = kzalloc(sizeof(*dev), M_WAITOK); 176219820Sjeff dev->parent = parent; 177219820Sjeff dev->class = class; 178219820Sjeff dev->devt = devt; 179219820Sjeff dev->driver_data = drvdata; 180219820Sjeff va_start(args, fmt); 181219820Sjeff kobject_set_name_vargs(&dev->kobj, fmt, args); 182219820Sjeff va_end(args); 183219820Sjeff device_register(dev); 184219820Sjeff 185219820Sjeff return (dev); 186219820Sjeff} 187219820Sjeff 188219820Sjeffint 189219820Sjeffkobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, 190219820Sjeff struct kobject *parent, const char *fmt, ...) 191219820Sjeff{ 192219820Sjeff va_list args; 193219820Sjeff int error; 194219820Sjeff 195219820Sjeff kobject_init(kobj, ktype); 196219820Sjeff kobj->ktype = ktype; 197219820Sjeff kobj->parent = parent; 198219820Sjeff kobj->name = NULL; 199219820Sjeff 200219820Sjeff va_start(args, fmt); 201219820Sjeff error = kobject_set_name_vargs(kobj, fmt, args); 202219820Sjeff va_end(args); 203219820Sjeff if (error) 204219820Sjeff return (error); 205219820Sjeff return kobject_add_complete(kobj, parent); 206219820Sjeff} 207219820Sjeff 208219820Sjeffstatic void 209219820Sjefflinux_file_dtor(void *cdp) 210219820Sjeff{ 211219820Sjeff struct linux_file *filp; 212219820Sjeff 213219820Sjeff filp = cdp; 214252041Sjhb filp->f_op->release(filp->f_vnode, filp); 215252041Sjhb vdrop(filp->f_vnode); 216219820Sjeff kfree(filp); 217219820Sjeff} 218219820Sjeff 219219820Sjeffstatic int 220219820Sjefflinux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 221219820Sjeff{ 222219820Sjeff struct linux_cdev *ldev; 223219820Sjeff struct linux_file *filp; 224219820Sjeff struct file *file; 225219820Sjeff int error; 226219820Sjeff 227219820Sjeff file = curthread->td_fpop; 228219820Sjeff ldev = dev->si_drv1; 229219820Sjeff if (ldev == NULL) 230219820Sjeff return (ENODEV); 231219820Sjeff filp = kzalloc(sizeof(*filp), GFP_KERNEL); 232219820Sjeff filp->f_dentry = &filp->f_dentry_store; 233219820Sjeff filp->f_op = ldev->ops; 234219820Sjeff filp->f_flags = file->f_flag; 235252041Sjhb vhold(file->f_vnode); 236252041Sjhb filp->f_vnode = file->f_vnode; 237219820Sjeff if (filp->f_op->open) { 238219820Sjeff error = -filp->f_op->open(file->f_vnode, filp); 239219820Sjeff if (error) { 240219820Sjeff kfree(filp); 241219820Sjeff return (error); 242219820Sjeff } 243219820Sjeff } 244219820Sjeff error = devfs_set_cdevpriv(filp, linux_file_dtor); 245219820Sjeff if (error) { 246219820Sjeff filp->f_op->release(file->f_vnode, filp); 247219820Sjeff kfree(filp); 248219820Sjeff return (error); 249219820Sjeff } 250219820Sjeff 251219820Sjeff return 0; 252219820Sjeff} 253219820Sjeff 254219820Sjeffstatic int 255219820Sjefflinux_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 256219820Sjeff{ 257219820Sjeff struct linux_cdev *ldev; 258219820Sjeff struct linux_file *filp; 259219820Sjeff struct file *file; 260219820Sjeff int error; 261219820Sjeff 262219820Sjeff file = curthread->td_fpop; 263219820Sjeff ldev = dev->si_drv1; 264219820Sjeff if (ldev == NULL) 265219820Sjeff return (0); 266219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 267219820Sjeff return (error); 268219820Sjeff filp->f_flags = file->f_flag; 269219820Sjeff 270219820Sjeff return (0); 271219820Sjeff} 272219820Sjeff 273219820Sjeffstatic int 274219820Sjefflinux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 275219820Sjeff struct thread *td) 276219820Sjeff{ 277219820Sjeff struct linux_cdev *ldev; 278219820Sjeff struct linux_file *filp; 279219820Sjeff struct file *file; 280219820Sjeff int error; 281219820Sjeff 282219820Sjeff file = curthread->td_fpop; 283219820Sjeff ldev = dev->si_drv1; 284219820Sjeff if (ldev == NULL) 285219820Sjeff return (0); 286219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 287219820Sjeff return (error); 288219820Sjeff filp->f_flags = file->f_flag; 289219820Sjeff /* 290219820Sjeff * Linux does not have a generic ioctl copyin/copyout layer. All 291219820Sjeff * linux ioctls must be converted to void ioctls which pass a 292219820Sjeff * pointer to the address of the data. We want the actual user 293219820Sjeff * address so we dereference here. 294219820Sjeff */ 295219820Sjeff data = *(void **)data; 296219820Sjeff if (filp->f_op->unlocked_ioctl) 297219820Sjeff error = -filp->f_op->unlocked_ioctl(filp, cmd, (u_long)data); 298219820Sjeff else 299219820Sjeff error = ENOTTY; 300219820Sjeff 301219820Sjeff return (error); 302219820Sjeff} 303219820Sjeff 304219820Sjeffstatic int 305219820Sjefflinux_dev_read(struct cdev *dev, struct uio *uio, int ioflag) 306219820Sjeff{ 307219820Sjeff struct linux_cdev *ldev; 308219820Sjeff struct linux_file *filp; 309219820Sjeff struct file *file; 310219820Sjeff ssize_t bytes; 311219820Sjeff int error; 312219820Sjeff 313219820Sjeff file = curthread->td_fpop; 314219820Sjeff ldev = dev->si_drv1; 315219820Sjeff if (ldev == NULL) 316219820Sjeff return (0); 317219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 318219820Sjeff return (error); 319219820Sjeff filp->f_flags = file->f_flag; 320219820Sjeff if (uio->uio_iovcnt != 1) 321219820Sjeff panic("linux_dev_read: uio %p iovcnt %d", 322219820Sjeff uio, uio->uio_iovcnt); 323219820Sjeff if (filp->f_op->read) { 324219820Sjeff bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, 325219820Sjeff uio->uio_iov->iov_len, &uio->uio_offset); 326219820Sjeff if (bytes >= 0) { 327219820Sjeff uio->uio_iov->iov_base += bytes; 328219820Sjeff uio->uio_iov->iov_len -= bytes; 329219820Sjeff uio->uio_resid -= bytes; 330219820Sjeff } else 331219820Sjeff error = -bytes; 332219820Sjeff } else 333219820Sjeff error = ENXIO; 334219820Sjeff 335219820Sjeff return (error); 336219820Sjeff} 337219820Sjeff 338219820Sjeffstatic int 339219820Sjefflinux_dev_write(struct cdev *dev, struct uio *uio, int ioflag) 340219820Sjeff{ 341219820Sjeff struct linux_cdev *ldev; 342219820Sjeff struct linux_file *filp; 343219820Sjeff struct file *file; 344219820Sjeff ssize_t bytes; 345219820Sjeff int error; 346219820Sjeff 347219820Sjeff file = curthread->td_fpop; 348219820Sjeff ldev = dev->si_drv1; 349219820Sjeff if (ldev == NULL) 350219820Sjeff return (0); 351219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 352219820Sjeff return (error); 353219820Sjeff filp->f_flags = file->f_flag; 354219820Sjeff if (uio->uio_iovcnt != 1) 355219820Sjeff panic("linux_dev_write: uio %p iovcnt %d", 356219820Sjeff uio, uio->uio_iovcnt); 357219820Sjeff if (filp->f_op->write) { 358219820Sjeff bytes = filp->f_op->write(filp, uio->uio_iov->iov_base, 359219820Sjeff uio->uio_iov->iov_len, &uio->uio_offset); 360219820Sjeff if (bytes >= 0) { 361219820Sjeff uio->uio_iov->iov_base += bytes; 362219820Sjeff uio->uio_iov->iov_len -= bytes; 363219820Sjeff uio->uio_resid -= bytes; 364219820Sjeff } else 365219820Sjeff error = -bytes; 366219820Sjeff } else 367219820Sjeff error = ENXIO; 368219820Sjeff 369219820Sjeff return (error); 370219820Sjeff} 371219820Sjeff 372219820Sjeffstatic int 373219820Sjefflinux_dev_poll(struct cdev *dev, int events, struct thread *td) 374219820Sjeff{ 375219820Sjeff struct linux_cdev *ldev; 376219820Sjeff struct linux_file *filp; 377219820Sjeff struct file *file; 378219820Sjeff int revents; 379219820Sjeff int error; 380219820Sjeff 381219820Sjeff file = curthread->td_fpop; 382219820Sjeff ldev = dev->si_drv1; 383219820Sjeff if (ldev == NULL) 384219820Sjeff return (0); 385219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 386219820Sjeff return (error); 387219820Sjeff filp->f_flags = file->f_flag; 388219820Sjeff if (filp->f_op->poll) 389219820Sjeff revents = filp->f_op->poll(filp, NULL) & events; 390219820Sjeff else 391219820Sjeff revents = 0; 392219820Sjeff 393219820Sjeff return (revents); 394219820Sjeff} 395219820Sjeff 396219820Sjeffstatic int 397219820Sjefflinux_dev_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 398219820Sjeff int nprot, vm_memattr_t *memattr) 399219820Sjeff{ 400219820Sjeff 401219820Sjeff /* XXX memattr not honored. */ 402219820Sjeff *paddr = offset; 403219820Sjeff return (0); 404219820Sjeff} 405219820Sjeff 406219820Sjeffstatic int 407219820Sjefflinux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, 408219820Sjeff vm_size_t size, struct vm_object **object, int nprot) 409219820Sjeff{ 410219820Sjeff struct linux_cdev *ldev; 411219820Sjeff struct linux_file *filp; 412219820Sjeff struct file *file; 413219820Sjeff struct vm_area_struct vma; 414219820Sjeff vm_paddr_t paddr; 415219820Sjeff vm_page_t m; 416219820Sjeff int error; 417219820Sjeff 418219820Sjeff file = curthread->td_fpop; 419219820Sjeff ldev = dev->si_drv1; 420219820Sjeff if (ldev == NULL) 421219820Sjeff return (ENODEV); 422219820Sjeff if (size != PAGE_SIZE) 423219820Sjeff return (EINVAL); 424219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 425219820Sjeff return (error); 426219820Sjeff filp->f_flags = file->f_flag; 427219820Sjeff vma.vm_start = 0; 428219820Sjeff vma.vm_end = PAGE_SIZE; 429219820Sjeff vma.vm_pgoff = *offset / PAGE_SIZE; 430219820Sjeff vma.vm_pfn = 0; 431219820Sjeff vma.vm_page_prot = 0; 432219820Sjeff if (filp->f_op->mmap) { 433219820Sjeff error = -filp->f_op->mmap(filp, &vma); 434219820Sjeff if (error == 0) { 435219820Sjeff paddr = (vm_paddr_t)vma.vm_pfn << PAGE_SHIFT; 436219820Sjeff *offset = paddr; 437219820Sjeff m = PHYS_TO_VM_PAGE(paddr); 438219820Sjeff *object = vm_pager_allocate(OBJT_DEVICE, dev, 439219820Sjeff PAGE_SIZE, nprot, *offset, curthread->td_ucred); 440219820Sjeff if (*object == NULL) 441219820Sjeff return (EINVAL); 442219820Sjeff if (vma.vm_page_prot != VM_MEMATTR_DEFAULT) 443219820Sjeff pmap_page_set_memattr(m, vma.vm_page_prot); 444219820Sjeff } 445219820Sjeff } else 446219820Sjeff error = ENODEV; 447219820Sjeff 448219820Sjeff return (error); 449219820Sjeff} 450219820Sjeff 451219820Sjeffstruct cdevsw linuxcdevsw = { 452219820Sjeff .d_version = D_VERSION, 453219820Sjeff .d_flags = D_TRACKCLOSE, 454219820Sjeff .d_open = linux_dev_open, 455219820Sjeff .d_close = linux_dev_close, 456219820Sjeff .d_read = linux_dev_read, 457219820Sjeff .d_write = linux_dev_write, 458219820Sjeff .d_ioctl = linux_dev_ioctl, 459219820Sjeff .d_mmap_single = linux_dev_mmap_single, 460219820Sjeff .d_mmap = linux_dev_mmap, 461219820Sjeff .d_poll = linux_dev_poll, 462219820Sjeff}; 463219820Sjeff 464219820Sjeffstatic int 465219820Sjefflinux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred, 466219820Sjeff int flags, struct thread *td) 467219820Sjeff{ 468219820Sjeff struct linux_file *filp; 469219820Sjeff ssize_t bytes; 470219820Sjeff int error; 471219820Sjeff 472219820Sjeff error = 0; 473219820Sjeff filp = (struct linux_file *)file->f_data; 474219820Sjeff filp->f_flags = file->f_flag; 475219820Sjeff if (uio->uio_iovcnt != 1) 476219820Sjeff panic("linux_file_read: uio %p iovcnt %d", 477219820Sjeff uio, uio->uio_iovcnt); 478219820Sjeff if (filp->f_op->read) { 479219820Sjeff bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, 480219820Sjeff uio->uio_iov->iov_len, &uio->uio_offset); 481219820Sjeff if (bytes >= 0) { 482219820Sjeff uio->uio_iov->iov_base += bytes; 483219820Sjeff uio->uio_iov->iov_len -= bytes; 484219820Sjeff uio->uio_resid -= bytes; 485219820Sjeff } else 486219820Sjeff error = -bytes; 487219820Sjeff } else 488219820Sjeff error = ENXIO; 489219820Sjeff 490219820Sjeff return (error); 491219820Sjeff} 492219820Sjeff 493219820Sjeffstatic int 494219820Sjefflinux_file_poll(struct file *file, int events, struct ucred *active_cred, 495219820Sjeff struct thread *td) 496219820Sjeff{ 497219820Sjeff struct linux_file *filp; 498219820Sjeff int revents; 499219820Sjeff 500219820Sjeff filp = (struct linux_file *)file->f_data; 501219820Sjeff filp->f_flags = file->f_flag; 502219820Sjeff if (filp->f_op->poll) 503219820Sjeff revents = filp->f_op->poll(filp, NULL) & events; 504219820Sjeff else 505219820Sjeff revents = 0; 506219820Sjeff 507219820Sjeff return (0); 508219820Sjeff} 509219820Sjeff 510219820Sjeffstatic int 511219820Sjefflinux_file_close(struct file *file, struct thread *td) 512219820Sjeff{ 513219820Sjeff struct linux_file *filp; 514219820Sjeff int error; 515219820Sjeff 516219820Sjeff filp = (struct linux_file *)file->f_data; 517219820Sjeff filp->f_flags = file->f_flag; 518219820Sjeff error = -filp->f_op->release(NULL, filp); 519219820Sjeff funsetown(&filp->f_sigio); 520219820Sjeff kfree(filp); 521219820Sjeff 522219820Sjeff return (error); 523219820Sjeff} 524219820Sjeff 525219820Sjeffstatic int 526219820Sjefflinux_file_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *cred, 527219820Sjeff struct thread *td) 528219820Sjeff{ 529219820Sjeff struct linux_file *filp; 530219820Sjeff int error; 531219820Sjeff 532219820Sjeff filp = (struct linux_file *)fp->f_data; 533219820Sjeff filp->f_flags = fp->f_flag; 534219820Sjeff error = 0; 535219820Sjeff 536219820Sjeff switch (cmd) { 537219820Sjeff case FIONBIO: 538219820Sjeff break; 539219820Sjeff case FIOASYNC: 540219820Sjeff if (filp->f_op->fasync == NULL) 541219820Sjeff break; 542219820Sjeff error = filp->f_op->fasync(0, filp, fp->f_flag & FASYNC); 543219820Sjeff break; 544219820Sjeff case FIOSETOWN: 545219820Sjeff error = fsetown(*(int *)data, &filp->f_sigio); 546219820Sjeff if (error == 0) 547219820Sjeff error = filp->f_op->fasync(0, filp, 548219820Sjeff fp->f_flag & FASYNC); 549219820Sjeff break; 550219820Sjeff case FIOGETOWN: 551219820Sjeff *(int *)data = fgetown(&filp->f_sigio); 552219820Sjeff break; 553219820Sjeff default: 554219820Sjeff error = ENOTTY; 555219820Sjeff break; 556219820Sjeff } 557219820Sjeff return (error); 558219820Sjeff} 559219820Sjeff 560219820Sjeffstruct fileops linuxfileops = { 561219820Sjeff .fo_read = linux_file_read, 562219820Sjeff .fo_poll = linux_file_poll, 563219820Sjeff .fo_close = linux_file_close, 564224914Skib .fo_ioctl = linux_file_ioctl, 565224914Skib .fo_chmod = invfo_chmod, 566224914Skib .fo_chown = invfo_chown, 567219820Sjeff}; 568219820Sjeff 569219820Sjeff/* 570219820Sjeff * Hash of vmmap addresses. This is infrequently accessed and does not 571219820Sjeff * need to be particularly large. This is done because we must store the 572219820Sjeff * caller's idea of the map size to properly unmap. 573219820Sjeff */ 574219820Sjeffstruct vmmap { 575219820Sjeff LIST_ENTRY(vmmap) vm_next; 576219820Sjeff void *vm_addr; 577219820Sjeff unsigned long vm_size; 578219820Sjeff}; 579219820Sjeff 580219820SjeffLIST_HEAD(vmmaphd, vmmap); 581219820Sjeff#define VMMAP_HASH_SIZE 64 582219820Sjeff#define VMMAP_HASH_MASK (VMMAP_HASH_SIZE - 1) 583219820Sjeff#define VM_HASH(addr) ((uintptr_t)(addr) >> PAGE_SHIFT) & VMMAP_HASH_MASK 584219820Sjeffstatic struct vmmaphd vmmaphead[VMMAP_HASH_SIZE]; 585219820Sjeffstatic struct mtx vmmaplock; 586219820Sjeff 587219820Sjeffstatic void 588219820Sjeffvmmap_add(void *addr, unsigned long size) 589219820Sjeff{ 590219820Sjeff struct vmmap *vmmap; 591219820Sjeff 592219820Sjeff vmmap = kmalloc(sizeof(*vmmap), GFP_KERNEL); 593219820Sjeff mtx_lock(&vmmaplock); 594219820Sjeff vmmap->vm_size = size; 595219820Sjeff vmmap->vm_addr = addr; 596219820Sjeff LIST_INSERT_HEAD(&vmmaphead[VM_HASH(addr)], vmmap, vm_next); 597219820Sjeff mtx_unlock(&vmmaplock); 598219820Sjeff} 599219820Sjeff 600219820Sjeffstatic struct vmmap * 601219820Sjeffvmmap_remove(void *addr) 602219820Sjeff{ 603219820Sjeff struct vmmap *vmmap; 604219820Sjeff 605219820Sjeff mtx_lock(&vmmaplock); 606219820Sjeff LIST_FOREACH(vmmap, &vmmaphead[VM_HASH(addr)], vm_next) 607219820Sjeff if (vmmap->vm_addr == addr) 608219820Sjeff break; 609219820Sjeff if (vmmap) 610219820Sjeff LIST_REMOVE(vmmap, vm_next); 611219820Sjeff mtx_unlock(&vmmaplock); 612219820Sjeff 613219820Sjeff return (vmmap); 614219820Sjeff} 615219820Sjeff 616219820Sjeffvoid * 617219820Sjeff_ioremap_attr(vm_paddr_t phys_addr, unsigned long size, int attr) 618219820Sjeff{ 619219820Sjeff void *addr; 620219820Sjeff 621219820Sjeff addr = pmap_mapdev_attr(phys_addr, size, attr); 622219820Sjeff if (addr == NULL) 623219820Sjeff return (NULL); 624219820Sjeff vmmap_add(addr, size); 625219820Sjeff 626219820Sjeff return (addr); 627219820Sjeff} 628219820Sjeff 629219820Sjeffvoid 630219820Sjeffiounmap(void *addr) 631219820Sjeff{ 632219820Sjeff struct vmmap *vmmap; 633219820Sjeff 634219820Sjeff vmmap = vmmap_remove(addr); 635219820Sjeff if (vmmap == NULL) 636219820Sjeff return; 637219820Sjeff pmap_unmapdev((vm_offset_t)addr, vmmap->vm_size); 638219820Sjeff kfree(vmmap); 639219820Sjeff} 640219820Sjeff 641219820Sjeff 642219820Sjeffvoid * 643219820Sjeffvmap(struct page **pages, unsigned int count, unsigned long flags, int prot) 644219820Sjeff{ 645219820Sjeff vm_offset_t off; 646219820Sjeff size_t size; 647219820Sjeff 648219820Sjeff size = count * PAGE_SIZE; 649219820Sjeff off = kmem_alloc_nofault(kernel_map, size); 650219820Sjeff if (off == 0) 651219820Sjeff return (NULL); 652219820Sjeff vmmap_add((void *)off, size); 653219820Sjeff pmap_qenter(off, pages, count); 654219820Sjeff 655219820Sjeff return ((void *)off); 656219820Sjeff} 657219820Sjeff 658219820Sjeffvoid 659219820Sjeffvunmap(void *addr) 660219820Sjeff{ 661219820Sjeff struct vmmap *vmmap; 662219820Sjeff 663219820Sjeff vmmap = vmmap_remove(addr); 664219820Sjeff if (vmmap == NULL) 665219820Sjeff return; 666219820Sjeff pmap_qremove((vm_offset_t)addr, vmmap->vm_size / PAGE_SIZE); 667219820Sjeff kmem_free(kernel_map, (vm_offset_t)addr, vmmap->vm_size); 668219820Sjeff kfree(vmmap); 669219820Sjeff} 670219820Sjeff 671219820Sjeffstatic void 672219820Sjefflinux_compat_init(void) 673219820Sjeff{ 674219820Sjeff struct sysctl_oid *rootoid; 675219820Sjeff int i; 676219820Sjeff 677219820Sjeff rootoid = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(), 678219820Sjeff OID_AUTO, "sys", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "sys"); 679219820Sjeff kobject_init(&class_root, &class_ktype); 680219820Sjeff kobject_set_name(&class_root, "class"); 681219820Sjeff class_root.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), 682219820Sjeff OID_AUTO, "class", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "class"); 683219820Sjeff kobject_init(&linux_rootdev.kobj, &dev_ktype); 684219820Sjeff kobject_set_name(&linux_rootdev.kobj, "device"); 685219820Sjeff linux_rootdev.kobj.oidp = SYSCTL_ADD_NODE(NULL, 686219820Sjeff SYSCTL_CHILDREN(rootoid), OID_AUTO, "device", CTLFLAG_RD, NULL, 687219820Sjeff "device"); 688219820Sjeff linux_rootdev.bsddev = root_bus; 689219820Sjeff miscclass.name = "misc"; 690219820Sjeff class_register(&miscclass); 691219820Sjeff INIT_LIST_HEAD(&pci_drivers); 692219820Sjeff INIT_LIST_HEAD(&pci_devices); 693219820Sjeff spin_lock_init(&pci_lock); 694219820Sjeff mtx_init(&vmmaplock, "IO Map lock", NULL, MTX_DEF); 695219820Sjeff for (i = 0; i < VMMAP_HASH_SIZE; i++) 696219820Sjeff LIST_INIT(&vmmaphead[i]); 697219820Sjeff} 698219820Sjeff 699219820SjeffSYSINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_init, NULL); 700