用户态与核态共享内存
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;
}