⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 map_driver.c

📁 为Linux虚拟内存驱动程序
💻 C
字号:
#define _VERSION__
#include <linux/kernel.h>
#include <linux/module.h>
#if CONFIG_MODVERSIOINS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
#include<linux/fs.h>
#include<linux/string.h>
#include<linux/errno.h>

#include<linux/mm.h>
#include<linux/vmalloc.h>
#include<linux/wrapper.h>
#include<linux/slab.h>
#include<asm/io.h>
#include<linux/mman.h>

#define MAPLEN (4096*10)
/* device open */
int mapdrv_open(struct inode *inode,struct file *file);
/* device close */
int mapdrv_release(struct inode *inode,struct file *file);
/*device mmap */
int mapdrv_mmap(struct file *file,struct vm_area_struct *vma);

/* vm area open */
void map_vopen(struct vm_area_struct *vma);
/* vm area close */
void map_vclose(struct vm_area_struct *vma);
/* vm area nopage */
struct page *map_nopage(struct vm_area_struct *vma,unsigned long address,int write_access);

static struct file_operations mapdrv_fops=
{
	owner  : THIS_MODULE,
	mmap   : mapdrv_mmap,
	open   : mapdrv_open,
	release:mapdrv_release,
};

static struct vm_operations_struct map_vm_ops=
{
	open  : map_vopen,
	close : map_vclose,
	nopage: map_nopage,
};
      
static int *vmalloc_area = NULL;     
        
static int major; // major number of device      

volatile void *vaddr_to_kaddr(volatile void *address)
{
  pgd_t *pgd; pmd_t *pmd; pte_t *ptep, pte;
  unsigned long va, ret = 0UL;
  va=VMALLOC_VMADDR((unsigned long)address);
  /* get the page directory. Use the kernel memory map. */
  pgd = pgd_offset_k(va);
  /* check whether we found an entry */
  if (!pgd_none(*pgd))
  {
   /* get the page middle directory */
   pmd = pmd_offset(pgd, va);
   /* check whether we found an entry */
   if (!pmd_none(*pmd))
   {
    /* get a pointer to the page table entry */
    ptep = pte_offset(pmd, va);
    pte = *ptep;
    /* check for a valid page */
    if (pte_present(pte))
      {
       /* get the address the page is refering to */
       ret = (unsigned long)page_address(pte_page(pte));
       /* add the offset within the page to the page address */                        ret |= (va & (PAGE_SIZE -1));
      }
   }
  }
   return((volatile void *)ret);
}



static int  __init mapdrv_init(void)
{
  
  unsigned long virt_addr;
  if ((major=register_chrdev(0, "mapdrv", &mapdrv_fops))<0) 
  {
  printk("mapdrv: unable to register character device\n");
  return (-EIO);
  }
   /* get a memory area that is only virtual contigous. */
    vmalloc_area=vmalloc(MAPLEN/*+2*PAGE_SIZE*/);


    for (virt_addr=(unsigned long)vmalloc_area;virt_addr<(unsigned long)(&(vmalloc_area[MAPLEN/sizeof(int)]));virt_addr+=PAGE_SIZE)
     {
     	     
    mem_map_reserve(virt_to_page(vaddr_to_kaddr((void *)virt_addr)));
     }
    /* set a hello message to kernel space for read by user */
     strcpy((char*)vmalloc_area,"hello world from kernel space !"); 
    
   
     printk("vmalloc_area at 0x%p (phys 0x%lx)\n", vmalloc_area,
     virt_to_phys((void *)vaddr_to_kaddr(vmalloc_area)));
        return(0);
}


static void __exit mapdrv_exit(void)
{
  unsigned long virt_addr;
  /* unreserve all pages */
 for (virt_addr=(unsigned long)vmalloc_area;virt_addr<(unsigned long)(&(vmalloc_area[MAPLEN/sizeof(int)]));virt_addr+=PAGE_SIZE)
  {
   mem_map_unreserve(virt_to_page(vaddr_to_kaddr((void *)virt_addr)));
  }
  /* and free the two areas */
  if (vmalloc_area)
    vfree(vmalloc_area);
 /* unregister the device */
  unregister_chrdev(major, "mapdrv");
  return;

}
/* device open method */
int mapdrv_open(struct inode *inode, struct file *file)
{
    MOD_INC_USE_COUNT;
    return(0);
}

/* device close method */
int mapdrv_release(struct inode *inode, struct file *file)
{
    MOD_DEC_USE_COUNT;
    return(0);
}

int mapdrv_mmap(struct file *file, struct vm_area_struct *vma)
{
   unsigned long offset = vma->vm_pgoff<<PAGE_SHIFT;

   unsigned long size = vma->vm_end - vma->vm_start;
   if (offset & ~PAGE_MASK)
   {
    printk("offset not aligned: %ld\n", offset);
    return -ENXIO;
   }
   if (size>MAPLEN)
   {
    printk("size too big\n");
    return(-ENXIO);
   }
  /*  only support shared mappings.*/
   if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
   {
    printk("writeable mappings must be shared, rejecting\n");
    return(-EINVAL);
   }
  /* do not want to have this area swapped out, lock it */
    vma->vm_flags |= VM_LOCKED;
   if (offset == 0)
   {
    vma->vm_ops = &map_vm_ops;
   /* call the open routine to increment the usage count */
    map_vopen(vma);
   }else
   {
     printk("offset out of range\n");
     return -ENXIO;
   }
   return(0);
}															       
/* open handler for vm area */
void map_vopen(struct vm_area_struct *vma)
{
  /* needed to prevent the unloading of the module while
  somebody still has memory mapped */
     MOD_INC_USE_COUNT;
}

/* close handler form vm area */
void map_vclose(struct vm_area_struct *vma)
{
     MOD_DEC_USE_COUNT;
}

/* page fault handler */
struct page *map_nopage(struct vm_area_struct *vma, unsigned long address, int write_access)
{
  unsigned long offset;
  unsigned long virt_addr;
  /* determine the offset within the vmalloc'd area  */
  offset = address - vma->vm_start + (vma->vm_pgoff<<PAGE_SHIFT);
  /* translate the vmalloc address to kmalloc address  */
  virt_addr = (unsigned long)vaddr_to_kaddr(&vmalloc_area[offset/sizeof(int)]);
  if (virt_addr == 0UL)
     {
     return((struct page *)0UL);
     }
  /* increment the usage count of the page */
  atomic_inc(&(virt_to_page(virt_addr)->count));
  printk("map_drv: page fault for offset 0x%lx (kseg x%lx)\n",offset, virt_addr);
   return(virt_to_page(virt_addr));
	  
}


  
	
module_init(mapdrv_init);
module_exit(mapdrv_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -