📄 dhahelper.c
字号:
/* Direct Hardware Access kernel helper (C) 2002 Alex Beregszaszi <alex@naxine.org> (C) 2002-2003 Nick Kurshev <nickols_k@mail.ru> Accessing hardware from userspace as USER (no root needed!) Tested on 2.2.x (2.2.19) and 2.4.x (2.4.3,2.4.17). License: GPL WARNING! THIS MODULE VIOLATES SEVERAL SECURITY LINES! DON'T USE IT ON PRODUCTION SYSTEMS, ONLY AT HOME, ON A "SINGLE-USER" SYSTEM. NO WARRANTY! IF YOU WANT TO USE IT ON PRODUCTION SYSTEMS THEN PLEASE READ 'README' FILE TO KNOW HOW TO PREVENT ANONYMOUS ACCESS TO THIS MODULE. Tech: Communication between userspace and kernelspace goes over character device using ioctl. Usage: mknod -m 666 /dev/dhahelper c 180 0 Also you can change the major number, setting the "dhahelper_major" module parameter, the default is 180, specified in dhahelper.h. Note: do not use other than minor==0, the module forbids it. TODO: * select (request?) a "valid" major number (from Linux project? ;) * make security * is pci handling needed? (libdha does this with lowlevel port funcs) * is mttr handling needed? * test on older kernels (2.0.x (?))*/#ifndef MODULE#define MODULE#endif#ifndef __KERNEL__#define __KERNEL__#endif#include <linux/config.h>#ifdef CONFIG_MODVERSION#define MODVERSION#include <linux/modversions.h>#endif#include <linux/version.h>#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/pagemap.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/wrapper.h>#include <linux/vmalloc.h>#include <linux/string.h>#include <linux/errno.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/unistd.h>#include <asm/uaccess.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)#include <linux/malloc.h>#else#include <linux/slab.h>#endif#include <linux/pci.h>#include <linux/ioport.h>#include <linux/init.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/io.h>#include <linux/mman.h>#include <linux/fs.h>#include <linux/unistd.h>#ifdef CONFIG_MTRR #include <asm/mtrr.h>#endif#ifdef CONFIG_DEVFS_FS#include <linux/devfs_fs_kernel.h>#endif#include "dhahelper.h"#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)#define DEV_MINOR(d) minor(d)#define pte_offset(p,a) pte_offset_kernel(p,a)#define LockPage(p) SetPageLocked(p)#define UnlockPage(p) ClearPageLocked(p)#else#define DEV_MINOR(d) MINOR(d)#endifMODULE_AUTHOR("Alex Beregszaszi <alex@naxine.org>, Nick Kurshev <nickols_k@mail.ru>, M錸s Rullg錼d <mru@users.sf.net>");MODULE_DESCRIPTION("Provides userspace access to hardware");#ifdef MODULE_LICENSEMODULE_LICENSE("GPL");#endifstatic int dhahelper_major = DEFAULT_MAJOR;MODULE_PARM(dhahelper_major, "i");MODULE_PARM_DESC(dhahelper_major, "Major number of dhahelper characterdevice");/* 0 = silent *//* 1 = report errors (default) *//* 2 = debug */static int dhahelper_verbosity = 1;MODULE_PARM(dhahelper_verbosity, "i");MODULE_PARM_DESC(dhahelper_verbosity, "Level of verbosity (0 = silent, 1 = only errors, 2 = debug)");static int dhahelper_open(struct inode *inode, struct file *file){ if (dhahelper_verbosity > 1) printk(KERN_DEBUG "dhahelper: device opened\n"); if (DEV_MINOR(inode->i_rdev) != 0) return -ENXIO;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) MOD_INC_USE_COUNT;#endif return 0;}static int dhahelper_release(struct inode *inode, struct file *file){ if (dhahelper_verbosity > 1) printk(KERN_DEBUG "dhahelper: device released\n"); if (DEV_MINOR(inode->i_rdev) != 0) return -ENXIO;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) MOD_DEC_USE_COUNT;#endif return 0;}static int dhahelper_get_version(int * arg){ int version = API_VERSION; if (copy_to_user(arg, &version, sizeof(int))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy to userspace\n"); return -EFAULT; } return 0;}static int dhahelper_port(dhahelper_port_t * arg){ dhahelper_port_t port; if (copy_from_user(&port, arg, sizeof(dhahelper_port_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } switch(port.operation) { case PORT_OP_READ: { switch(port.size) { case 1: port.value = inb(port.addr); break; case 2: port.value = inw(port.addr); break; case 4: port.value = inl(port.addr); break; default: if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: invalid port read size (%d)\n", port.size); return -EINVAL; } break; } case PORT_OP_WRITE: { switch(port.size) { case 1: outb(port.value, port.addr); break; case 2: outw(port.value, port.addr); break; case 4: outl(port.value, port.addr); break; default: if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: invalid port write size (%d)\n", port.size); return -EINVAL; } break; } default: if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: invalid port operation (%d)\n", port.operation); return -EINVAL; } /* copy back only if read was performed */ if (port.operation == PORT_OP_READ) if (copy_to_user(arg, &port, sizeof(dhahelper_port_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy to userspace\n"); return -EFAULT; } return 0;}/*******************************//* Memory management functions *//* from kernel:/drivers/media/video/bttv-driver.c *//*******************************/#define MDEBUG(x) do { } while(0) /* Debug memory management *//* [DaveM] I've recoded most of this so that: * 1) It's easier to tell what is happening * 2) It's more portable, especially for translating things * out of vmalloc mapped areas in the kernel. * 3) Less unnecessary translations happen. * * The code used to assume that the kernel vmalloc mappings * existed in the page tables of every process, this is simply * not guarenteed. We now use pgd_offset_k which is the * defined way to get at the kernel page tables. *//* Given PGD from the address space's page table, return the kernel * virtual mapping of the physical memory mapped at ADR. */static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr){ unsigned long ret = 0UL; pmd_t *pmd; pte_t *ptep, pte; if (!pgd_none(*pgd)) { pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { ptep = pte_offset(pmd, adr); pte = *ptep; if(pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE - 1)); } } } MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); return ret;}static inline unsigned long uvirt_to_bus(unsigned long adr) { unsigned long kva, ret; kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); ret = virt_to_bus((void *)kva); MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); return ret;}static inline unsigned long uvirt_to_pa(unsigned long adr) { unsigned long kva, ret; kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); ret = virt_to_phys((void *)kva); MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); return ret;}static inline unsigned long kvirt_to_bus(unsigned long adr) { unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = uvirt_to_kva(pgd_offset_k(va), va); ret = virt_to_bus((void *)kva); MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); return ret;}/* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. */static inline unsigned long kvirt_to_pa(unsigned long adr) { unsigned long va, kva, ret; va = VMALLOC_VMADDR(adr); kva = uvirt_to_kva(pgd_offset_k(va), va); ret = __pa(kva); MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); return ret;}static void * rvmalloc(signed long size){ void * mem; unsigned long adr, page; mem=vmalloc_32(size); if (mem) { memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr=(unsigned long) mem; while (size > 0) { page = kvirt_to_pa(adr); mem_map_reserve(virt_to_page(__va(page))); adr+=PAGE_SIZE; size-=PAGE_SIZE; } } return mem;}static int pag_lock(unsigned long addr){ unsigned long page; unsigned long kva; kva = uvirt_to_kva(pgd_offset(current->mm, addr), addr); if(kva) { lock_it: page = uvirt_to_pa((unsigned long)addr); LockPage(virt_to_page(__va(page))); SetPageReserved(virt_to_page(__va(page))); } else { copy_from_user(&page,(char *)addr,1); /* try access it */ kva = uvirt_to_kva(pgd_offset(current->mm, addr), addr); if(kva) goto lock_it; else return EPERM; } return 0;}static int pag_unlock(unsigned long addr){ unsigned long page; unsigned long kva; kva = uvirt_to_kva(pgd_offset(current->mm, addr), addr); if(kva) { page = uvirt_to_pa((unsigned long)addr); UnlockPage(virt_to_page(__va(page))); ClearPageReserved(virt_to_page(__va(page))); return 0; } return EPERM;}static void rvfree(void * mem, signed long size){ unsigned long adr, page; if (mem) { adr=(unsigned long) mem; while (size > 0) { page = kvirt_to_pa(adr); mem_map_unreserve(virt_to_page(__va(page))); adr+=PAGE_SIZE; size-=PAGE_SIZE; } vfree(mem); }}static int dhahelper_virt_to_phys(dhahelper_vmi_t *arg){ dhahelper_vmi_t mem; unsigned long i,nitems; char *addr; if (copy_from_user(&mem, arg, sizeof(dhahelper_vmi_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } nitems = mem.length / PAGE_SIZE; if(mem.length % PAGE_SIZE) nitems++; addr = mem.virtaddr; for(i=0;i<nitems;i++) { unsigned long result; result = uvirt_to_pa((unsigned long)addr); if (copy_to_user(&mem.realaddr[i], &result, sizeof(unsigned long))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy to userspace\n"); return -EFAULT; } addr += PAGE_SIZE; } return 0;}static int dhahelper_virt_to_bus(dhahelper_vmi_t *arg){ dhahelper_vmi_t mem; unsigned long i,nitems; char *addr; if (copy_from_user(&mem, arg, sizeof(dhahelper_vmi_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } nitems = mem.length / PAGE_SIZE; if(mem.length % PAGE_SIZE) nitems++; addr = mem.virtaddr; for(i=0;i<nitems;i++) { unsigned long result; result = uvirt_to_bus((unsigned long)addr); if (copy_to_user(&mem.realaddr[i], &result, sizeof(unsigned long))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy to userspace\n"); return -EFAULT; } addr += PAGE_SIZE; } return 0;}static int dhahelper_alloc_pa(dhahelper_mem_t *arg){ dhahelper_mem_t mem; if (copy_from_user(&mem, arg, sizeof(dhahelper_mem_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } mem.addr = rvmalloc(mem.length); if (copy_to_user(arg, &mem, sizeof(dhahelper_mem_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy to userspace\n"); return -EFAULT; } return 0;}static int dhahelper_free_pa(dhahelper_mem_t *arg){ dhahelper_mem_t mem; if (copy_from_user(&mem, arg, sizeof(dhahelper_mem_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } rvfree(mem.addr,mem.length); return 0;}static int dhahelper_lock_mem(dhahelper_mem_t *arg){ dhahelper_mem_t mem; int retval; unsigned long i,nitems,addr; if (copy_from_user(&mem, arg, sizeof(dhahelper_mem_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } nitems = mem.length / PAGE_SIZE; if(mem.length % PAGE_SIZE) nitems++; addr = (unsigned long)mem.addr; for(i=0;i<nitems;i++) { retval = pag_lock((unsigned long)addr); if(retval) { unsigned long j; addr = (unsigned long)mem.addr; for(j=0;j<i;j++) { pag_unlock(addr); addr += PAGE_SIZE; } return retval; } addr += PAGE_SIZE; } return 0;}static int dhahelper_unlock_mem(dhahelper_mem_t *arg){ dhahelper_mem_t mem; int retval; unsigned long i,nitems,addr; if (copy_from_user(&mem, arg, sizeof(dhahelper_mem_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } nitems = mem.length / PAGE_SIZE; if(mem.length % PAGE_SIZE) nitems++; addr = (unsigned long)mem.addr; for(i=0;i<nitems;i++) { retval = pag_unlock((unsigned long)addr); if(retval) return retval; addr += PAGE_SIZE; } return 0;}static struct dha_irq { spinlock_t lock; long flags; int handled; int rcvd; volatile u32 *ack_addr; u32 ack_data; struct pci_dev *dev; wait_queue_head_t wait; unsigned long count;} dha_irqs[256];static void dhahelper_irq_handler(int irq, void *dev_id, struct pt_regs *regs){ spin_lock_irqsave(&dha_irqs[irq].lock, dha_irqs[irq].flags); if(dha_irqs[irq].handled){ dha_irqs[irq].rcvd = 1; dha_irqs[irq].count++; if(dha_irqs[irq].ack_addr){ *dha_irqs[irq].ack_addr = dha_irqs[irq].ack_data; mb(); } wake_up_interruptible(&dha_irqs[irq].wait); } spin_unlock_irqrestore(&dha_irqs[irq].lock, dha_irqs[irq].flags);}static int dhahelper_install_irq(dhahelper_irq_t *arg){ dhahelper_irq_t my_irq; struct pci_dev *pci; long rlen; int retval; long ack_addr; int irqn; if (copy_from_user(&my_irq, arg, sizeof(dhahelper_irq_t))) { if (dhahelper_verbosity > 0) printk(KERN_ERR "dhahelper: failed copy from userspace\n"); return -EFAULT; } if(!(pci = pci_find_slot(my_irq.bus, PCI_DEVFN(my_irq.dev, my_irq.func)))) return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -