mmap学习

用户态与核态共享内存

kmmap.h



#define PAGE_SHIFT_4K 12
#define KMMAP_MEM_MB_SHIFT 20
#define KMMAP_MEM_GB_SHIFT 30
#define KMMAP_MEM_MB_MASK 0xFFFFF


#define KMMAP_MEM_TOTAL_SZIE (((unsigned long)4) << KMMAP_MEM_GB_SHIFT)
#define KMMAP_MEM_BLOCK_SIZE (1 << KMMAP_MEM_MB_SHIFT)
#define KMMAP_MEM_BLOCK_ORDER 8
#define KMMAP_MEM_PAGE_TO_MB_SHIFT (KMMAP_MEM_MB_SHIFT - PAGE_SHIFT_4K)


#define KMMAP_MEM_MAX_BLOCK_COUNT (unsigned int)(KMMAP_MEM_TOTAL_SZIE >> KMMAP_MEM_MB_SHIFT)


#define KMMAP_MEM_ALLOC 1005
#define KMMAP_MEM_FREE 1006
#define KMMAP_MEM_SET_BLOCK 1007
#define KMMAP_MEM_CLEAR_BLOCK 1008


/* 描述vphy的结构体 */
typedef struct kmmap_mem_block_s
{
    uint32_t block_offset; /* 1M为单位的偏移量 */
    uint32_t block_count;  /* 1M为单位的内存块数量 */
}kmmap_mem_block_t;


/* 获取或者设置vphy时的结构体 */
typedef struct kmmap_mem_block_set_s
{
    kmmap_mem_block_t mem_block; /* 希望映射到哪一个vphy地址端 */
    uint64_t phy_addr;           /* vphy对应的物理地址的首地址 */
}kmmap_mem_block_set_t;


extern uint64_t kmmap_offeset_to_phy(uint64_t offset);


#endif


kmmap.c


#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>    /* printk() */
#include <linux/slab.h>        /* kmalloc() */
#include <linux/fs.h>        /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>    /* O_ACCMODE */
#include <linux/aio.h>
#include <asm/uaccess.h>
#include <linux/mm.h>        /* everything */
#include <linux/errno.h>    /* error codes */
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include "kmmap.h"






uint64_t *kmmap_phyblock;
unsigned long kmmap_phyblock_phyaddr;
module_param(kmmap_phyblock_phyaddr, ulong, S_IRUGO);


struct semaphore mem_op_sem;


uint64_t last_index;


dev_t mem_op_dev;
static struct cdev kmmap_cdev;
static struct class *kmmap_class;
static struct device *kmmap_device;
int  kmmap_device_major = 0;


void kmmap_vma_open(struct vm_area_struct *vma)
{
}


void kmmap_vma_close(struct vm_area_struct *vma)
{
}


int kmmap_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
    uint32_t block_index;
    uint32_t offset;
    uint32_t start_vphy_offset = vma->vm_pgoff >> KMMAP_MEM_PAGE_TO_MB_SHIFT;;
    
    block_index = start_vphy_offset + (((unsigned long)(vmf->virtual_address) - vma->vm_start) >> KMMAP_MEM_MB_SHIFT);
    offset = ((unsigned int)((unsigned long)(vmf->virtual_address) - vma->vm_start)) & KMMAP_MEM_MB_MASK;


    printk(KERN_INFO"kmmap_vma_fault: vmf->virtual_address %p, vma %p, vma->vm_start %lx, vma->vm_end %lx, start_vphy_offset %d, block_index %d, offset %d\n",
        vmf->virtual_address, vma, vma->vm_start, vma->vm_end, start_vphy_offset, block_index, offset);
    if (0 == kmmap_phyblock[block_index])
    {
        printk(KERN_INFO"kmmap_vma_fault, block_index is empty %d:\n", block_index);
        return KMMAP_ERROR;
    }


    vmf->page = virt_to_page(phys_to_virt(kmmap_phyblock[block_index] + offset));
    return KMMAP_SUCCESS;
    
}


struct vm_operations_struct kmmap_vm_ops = {
    .open =     kmmap_vma_open,
    .close =    kmmap_vma_close,
    .fault = kmmap_vma_fault,
};




uint64_t kmmap_offeset_to_phy(uint64_t offset)
{
    uint32_t memblock_index = (uint32_t)(offset >> KMMAP_MEM_MB_SHIFT);
    uint32_t block_offset = ((uint32_t)offset) & KMMAP_MEM_MB_MASK;
    
    return kmmap_phyblock[memblock_index] + block_offset;
}
EXPORT_SYMBOL(kmmap_offeset_to_phy);


int kmmap_mmap(struct file *filp, struct vm_area_struct *vma)
{
    uint32_t i;
    uint32_t memblock_count = (vma->vm_end - vma->vm_start) >> KMMAP_MEM_MB_SHIFT;
    int ret;
    unsigned long vm_addr;
    unsigned int vphy_index;
    


    vphy_index = vma->vm_pgoff >> KMMAP_MEM_PAGE_TO_MB_SHIFT;
    printk(KERN_INFO"try map mem from %lx to %lx, cout %d vphy index %x, start phy %llx\n",
        vma->vm_start, vma->vm_end, memblock_count, vphy_index, kmmap_phyblock[vphy_index]);
    if ((last_index != 0) && (vphy_index != last_index ))
    {
        printk(KERN_ERR"kmmap_mmap vm_pgoff %x != last_index %llx \n",
            vphy_index, last_index);
    }


    for (i = 0; i < memblock_count; i++)
    {
        
        vm_addr = vma->vm_start + (i << KMMAP_MEM_MB_SHIFT);
        printk(KERN_INFO"try map mem %lx to phy %llx, vphy_index %d\n",
            vm_addr, kmmap_phyblock[vphy_index + i], vphy_index);
    
        ret = remap_pfn_range(vma, vm_addr, kmmap_phyblock[vphy_index + i] >> PAGE_SHIFT_4K, 
            KMMAP_MEM_BLOCK_SIZE, vma->vm_page_prot);
        if (0 != ret)
        {
            printk(KERN_ERR"remap_pfn_range failed. try remap %lx to memblock index =%d. ret = %d \n", 
                vm_addr, i, ret);
            return ret;
        }
    }
    
    vma->vm_ops = &kmmap_vm_ops;
    vma->vm_flags |= VM_RESERVED | VM_SHARED  | VM_IO;
    vma->vm_private_data = (void *)last_index;
    return 0;
}


static int kmmap_open(struct inode *inode, struct file *file)
{
    return KMMAP_SUCCESS;
}
static int kmmap_release(struct inode *inode, struct file *file)
{
    return KMMAP_SUCCESS;
}




int kmmap_mem_free(kmmap_mem_block_t *mem_block)
{
    int i ;
    void *virt_addr;


    printk(KERN_INFO"kmmap_mem_free from %x count %x\n", mem_block->block_offset,
        mem_block->block_count);
    for (i = 0; i < mem_block->block_count; i++)
    {
        if (0 == kmmap_phyblock[mem_block->block_offset + i])
        {
            continue;
        }


        printk(KERN_INFO"kmmap_mem_free vphy index %x, addr %llx\n", 
            mem_block->block_offset + i, kmmap_phyblock[mem_block->block_offset + i]);
        virt_addr = phys_to_virt(kmmap_phyblock[mem_block->block_offset + i]);
        kfree(virt_addr);
        kmmap_phyblock[mem_block->block_offset + i] = 0;
    }
    return KMMAP_SUCCESS;
}


int kmmap_mem_alloc(kmmap_mem_block_t *mem_block)
{
    int i ;
    void *virt_addr;


    printk(KERN_INFO"kmmap_mem_alloc from %x count %x\n", mem_block->block_offset,
        mem_block->block_count);


    for(i = 0; i < mem_block->block_count; i++)
    {
        /* 尝试申请的段vphy 表非空,需要先释放才行 */
        if (kmmap_phyblock[mem_block->block_offset + i] != 0)
        {
            printk(KERN_ERR"kmmap_mem_alloc index=%d, phy not empty!\n", i);
            return KMMAP_ERROR;
        }
    }
    
    for (i = 0; i < mem_block->block_count; i++)
    {
        
        virt_addr = kmalloc(1 << KMMAP_MEM_MB_SHIFT, GFP_KERNEL);
        if (NULL == virt_addr)
        {
            printk(KERN_ERR"ERROR - Unable to phy block, index=%d\n", i);
            goto err;
        }


        printk(KERN_INFO"kmmap_mem_alloc vphy index %x, addr %llx\n", 
            mem_block->block_offset + i, virt_to_phys(virt_addr));
        kmmap_phyblock[mem_block->block_offset + i] = virt_to_phys(virt_addr);   
        
    }


    last_index = mem_block->block_offset;
    return KMMAP_SUCCESS;
err:
    kmmap_mem_free(mem_block);
    return KMMAP_ERROR;
    
}




int kmmap_mem_block_set(kmmap_mem_block_set_t *mem_block_set)
{
    int i;
    uint32_t block_offset = mem_block_set->mem_block.block_offset; 
    uint32_t block_count = mem_block_set->mem_block.block_count;


    for(i = 0; i < block_offset; i++)
    {
        /* 尝试申请的段vphy 表非空,需要先释放才行 */
        if (kmmap_phyblock[block_offset + i] != 0)
        {
            printk(KERN_ERR"kmmap_mem_block_set index=%d, phy not empty!\n", i);
            return KMMAP_ERROR;
        }
    }


    for (i = 0; i < block_count; i++)
    {
        kmmap_phyblock[block_offset + i] = mem_block_set->phy_addr + (i << KMMAP_MEM_MB_SHIFT);
    }
    last_index = 0;
    
    return KMMAP_SUCCESS;  
}


int kmmap_mem_block_clear(kmmap_mem_block_t *mem_block)
{
    int i;
    uint32_t block_offset = mem_block->block_offset; 
    uint32_t block_count = mem_block->block_count;


    for (i = 0; i < block_count; i++)
    {
        kmmap_phyblock[block_offset + i] = 0;
    }
  
    return KMMAP_SUCCESS;  
}


long kmmap_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
    kmmap_mem_block_t mem_block;
    kmmap_mem_block_set_t mem_block_set;
    int ret;
    
    down(&mem_op_sem);
    switch(cmd) 
    {
        
        case KMMAP_MEM_ALLOC:
            ret = copy_from_user((void *)&mem_block, (void __user*)arg, sizeof(mem_block));
            if (ret)
            {
                printk(KERN_ERR"ERROR - copy_from_user\n");
                goto out;
            }
            ret = kmmap_mem_alloc(&mem_block);
            goto out;
            
        case KMMAP_MEM_FREE:
            ret =  copy_from_user((void *)&mem_block, (void __user*)arg, sizeof(mem_block));
            if (ret)
            {
                printk(KERN_ERR"ERROR - copy_from_user\n");
                goto out;
            }
            ret = kmmap_mem_free(&mem_block);
            goto out;


        case KMMAP_MEM_CLEAR_BLOCK:
            ret = copy_from_user((void *)&mem_block, (void __user*)arg, sizeof(mem_block));
            if (ret)
            {
                printk(KERN_ERR"ERROR - copy_from_user\n");
                goto out;
            }
            ret = kmmap_mem_block_clear(&mem_block); 
            goto out;


        case KMMAP_MEM_SET_BLOCK:
            ret = copy_from_user((void *)&mem_block_set, (void __user*)arg, sizeof(mem_block_set));
            if (ret)
            {
                printk(KERN_ERR"ERROR - copy_from_user\n");
                goto out;
            }
            ret = kmmap_mem_block_set(&mem_block_set); 
            goto out;


        default:    
            ret = KMMAP_ERROR;
            goto out;
    }


out:
    up(&mem_op_sem);
    return ret;
}


static const struct file_operations kmmap_fops = {
    .owner  = THIS_MODULE,
    .open  = kmmap_open,
    .unlocked_ioctl = kmmap_ioctl,
    //.ioctl = kmmap_ioctl,
    .release = kmmap_release,
    .mmap = kmmap_mmap,
};


static int __init kmmap_init (void)
{
    int result;
    dev_t dev_id;
    
    printk(KERN_INFO"kmmap_init: total mem %lx, memblock size %x, max memblock count %x\n",
        KMMAP_MEM_TOTAL_SZIE, KMMAP_MEM_BLOCK_SIZE, KMMAP_MEM_MAX_BLOCK_COUNT);


    /* 初始化vphy表 */    
    kmmap_phyblock = kmalloc(sizeof(uint64_t)*KMMAP_MEM_MAX_BLOCK_COUNT, GFP_KERNEL);
    if (NULL == kmmap_phyblock)
    {
        printk(KERN_ERR"ERROR - Unable to create kmmap_phyblock\n");
        return KMMAP_ERROR;
    }


    
    memset(kmmap_phyblock, 0, sizeof(uint64_t)*KMMAP_MEM_MAX_BLOCK_COUNT);
    kmmap_phyblock_phyaddr = (unsigned long)virt_to_phys(kmmap_phyblock);
    init_MUTEX (&mem_op_sem);




    /* 初始化ioctl字符设备 */
    result = alloc_chrdev_region(&dev_id, 0, 1, KMMAP_NAME);
    kmmap_device_major = MAJOR(dev_id);
    
    if(result){
        printk("kmmap chrdev region failed\n");
        result = -EBUSY;
        goto err_chrdev_region;
    }
    
    cdev_init(&kmmap_cdev, &kmmap_fops);
    result = cdev_add(&kmmap_cdev, dev_id, 1);
    if (result) {
        printk("diskio cdev add failed\n");
        result = -EBUSY;
        goto err_cdev_add;
    }
    
    kmmap_class = class_create(THIS_MODULE, KMMAP_NAME);
    if (IS_ERR(kmmap_class)) {
        printk("%s: class_create failed\n", KMMAP_NAME);
        result = PTR_ERR(kmmap_class);
        goto err_class_create;
    }
    
    kmmap_device = device_create(kmmap_class, NULL, dev_id,  NULL, "%s", KMMAP_NAME);
    if (IS_ERR(kmmap_device)) {
        printk("%s: class_device_create failed\n", KMMAP_NAME);
        result = PTR_ERR(kmmap_device);
        goto err_device_create;
    }
    
    return 0;
err_device_create:
    class_destroy(kmmap_class);
err_class_create:
    cdev_del(&kmmap_cdev);
err_cdev_add:
    unregister_chrdev_region(dev_id, 1);
err_chrdev_region:      
    if (kmmap_phyblock)
    {
        kfree(kmmap_phyblock);
    }
    return result;
}


static void __exit kmmap_exit (void)
{
    dev_t dev_id = MKDEV(kmmap_device_major, 0);
    device_destroy(kmmap_class, dev_id);
    class_destroy(kmmap_class);
    cdev_del(&kmmap_cdev);
    unregister_chrdev_region(dev_id, 1);
    if (kmmap_phyblock)
    {
        kfree(kmmap_phyblock);
    }
}


module_init(kmmap_init);
module_exit(kmmap_exit);
MODULE_LICENSE("GPL");


用户态测试

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "kmmap.h"


int main(int argc,char* argv[])
{
    uint64_t *vphy_list;
    uint64_t vphy_list_phy_addr;
    uint32_t *kernel_buf;
    char *kernel_char_buf;
    uint32_t *keep_buf;
    int ret;
    kmmap_mem_block_t mem_block;
    kmmap_mem_block_set_t mem_block_set;
    char sys_buf[128] = {0};


    
    
    int fd = open(KMMAP_DEVICE_PATH, O_RDWR);
    if (fd < 0)
    {
        printf("failed to open device %s\n", KMMAP_DEVICE_PATH);
        return -1;
    }


    sscanf(argv[1], "%lx", &vphy_list_phy_addr);


    /* 将内核vphy表映射到vhpy的0-1M */
    mem_block_set.mem_block.block_count = 1;
    mem_block_set.mem_block.block_offset = 0;
    mem_block_set.phy_addr = vphy_list_phy_addr;


    /* 映射vphy表 */
    ret = ioctl(fd, KMMAP_MEM_SET_BLOCK, &mem_block_set);
    if (ret)
    {
        printf("failed to ioctl KMMAP_MEM_SET_BLOCK, ret %d\n", ret);
        goto close_file;
    }
    
    vphy_list = mmap(NULL, 1 << KMMAP_MEM_MB_SHIFT, 
        PROT_READ, MAP_SHARED, fd, 0);
    if (MAP_FAILED == vphy_list)
    {
        printf("failed to mmap vphy list, error %s\n", strerror(errno));
        ret = -1;
        goto close_file;
    }


    if (vphy_list[0] != vphy_list_phy_addr)
    {
        printf("vphy_list_phy_addr[0] != vphy_list_phy_addr\n");
        ret = -1;
        goto umap_vphy;
    }


    /* 申请2M 内存用于io,放到1-3M */
    mem_block.block_count = 2;
    mem_block.block_offset = 1;
    ret = ioctl(fd, KMMAP_MEM_ALLOC, &mem_block);
    if (ret)
    {
        printf("failed to ioctl KMMAP_MEM_ALLOC, ret %d\n", ret);
        goto umap_vphy;
    }
    /* 映射1-3 */
    kernel_buf = mmap(NULL, 2 << KMMAP_MEM_MB_SHIFT, 
        PROT_READ|PROT_WRITE, MAP_SHARED , 
        fd, 1 << KMMAP_MEM_MB_SHIFT);
    if (MAP_FAILED == kernel_buf)
    {
        printf("failed to mmap kernel alloc men, error %s\n", strerror(errno));
        ret = -1;
        goto free_kernel_mem;
    }


    printf("vphy_list %lx, kernel_buf %lx\n", vphy_list, kernel_buf);
    /* 针对第多个段进行设置,防止多个段时无法正常使用 */
    kernel_buf[0] = 0x12345678;


    kernel_char_buf = (char *)kernel_buf;
    kernel_char_buf += (1 << KMMAP_MEM_MB_SHIFT);
    /* uint32,所以对2M位置的读写需要/4 */
    *((uint32_t *)(kernel_char_buf)) = 0x87654321;


    /* 利用memop.ko 读取对应的物理地址的数值 */
    sprintf(sys_buf, "echo \"0x%lx\" > /proc/mem_op/read_user \n", vphy_list[1]);
    system(sys_buf);
    sprintf(sys_buf, "echo \"0x%lx\" > /proc/mem_op/read_user\n", vphy_list[2]);
    system(sys_buf);




    /* 将保留内存1G的位置表映射到vhpy的3-4M */
    mem_block_set.mem_block.block_count = 1;
    mem_block_set.mem_block.block_offset = 3;
    mem_block_set.phy_addr = ((unsigned long)1 << KMMAP_MEM_GB_SHIFT);


    /* 映射vphy表 */
    ret = ioctl(fd, KMMAP_MEM_SET_BLOCK, &mem_block_set);
    if (ret)
    {
        printf("failed to ioctl KMMAP_MEM_SET_BLOCK, ret %d\n", ret);
        goto umap_kernel_buf;
    }


    /* 映射3-4 */
    keep_buf = mmap(NULL, 1 << KMMAP_MEM_MB_SHIFT, 
        PROT_READ|PROT_WRITE, MAP_SHARED, fd, 3 << KMMAP_MEM_MB_SHIFT);
    if (NULL == keep_buf)
    {
        printf("failed to mmap kernel keep men, error %s\n", strerror(errno));
        ret = -1;
        goto umap_kernel_buf;
    }


    /* 读取保留内存 */
    keep_buf[0] = 0x43219876;
    //sprintf(sys_buf, "echo \"0x%lx\" > /proc/mem_op/read \n", vphy_list[3]);
    //system(sys_buf);




    if (NULL != keep_buf)
    {
        munmap(keep_buf, 1 << KMMAP_MEM_MB_SHIFT);
    }
   


umap_kernel_buf:
    
    if (NULL != kernel_buf)
    {
        munmap(kernel_buf, 2 << KMMAP_MEM_MB_SHIFT);
    }
    
free_kernel_mem:
    mem_block.block_count = 2;
    mem_block.block_offset = 1;
    ioctl(fd, KMMAP_MEM_FREE, &mem_block);


umap_vphy:
    if (NULL != vphy_list)
    {
        munmap(vphy_list, 1 << KMMAP_MEM_MB_SHIFT);
    }
    
close_file:
    if (fd >= 0)
    {
        close(fd);
    }


    return ret;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值