📄 memalloc.c.svn-base
字号:
/*-------------------------------------------------------------------------------- ---- This software is confidential and proprietary and may be used ---- only as expressly authorized by a licensing agreement from ---- ---- Hantro Products Oy. ---- ---- In the event of publication, the following notice is applicable: ---- ---- (C) COPYRIGHT 2002 HANTRO PRODUCTS OY ---- ALL RIGHTS RESERVED ---- ---- The entire notice above must be reproduced on all copies. ---- -------------------------------------------------------------------------------------- Project : Penguin---- Abstract : Encoder device driver (kernel module)-------------------------------------------------------------------------------------- Version control information, please leave untouched.---- $RCSfile$-- $Author$-- $Date$-- $Revision$-- $Name$--------------------------------------------------------------------------------*/#include <linux/kernel.h>#include <linux/module.h>/* needed for __init,__exit directives */#include <linux/init.h>/* needed for remap_page_range */#include <linux/mm.h>/* obviously, for kmalloc */#include <linux/slab.h>/* for struct file_operations, register_chrdev() */#include <linux/fs.h>/* standard error codes */#include <linux/errno.h>/* this header files wraps some common module-space operations ... here we use mem_map_reserve() macro *///#include <linux/wrapper.h>/* needed for virt_to_phys() */#include <asm/io.h>#include <asm/uaccess.h>#include <linux/ioport.h>#include <linux/list.h>/* our own stuff */#include "memalloc.h"/* module description */MODULE_AUTHOR("Nagy Attila Jozsef, Hantro");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("RAM allocation");static int memalloc_major = 188; /* and this is our MAJOR *//* here's all the must remember stuff */struct allocation{ struct list_head list; void *buffer; unsigned int order; int fid;};struct list_head heap_list;static kmem_cache_t *memalloc_cache;static spinlock_t mem_lock = SPIN_LOCK_UNLOCKED;static struct allocation *FindEntryByBus(unsigned long busaddr);static int AllocMemory(unsigned long *busaddr, unsigned int size);static int FreeMemory(unsigned long busaddr);static int MapBuffer(struct file *filp, struct vm_area_struct *vma);/* VM operations */static struct page *memalloc_vm_nopage(struct vm_area_struct *vma, unsigned long address, int write_access){ PDEBUG("memalloc_vm_nopage: problem with mem access\n"); return NOPAGE_SIGBUS; /* send a SIGBUS */}static void memalloc_vm_open(struct vm_area_struct *vma){ //MOD_INC_USE_COUNT; PDEBUG("memalloc_vm_open:\n");}static void memalloc_vm_close(struct vm_area_struct *vma){ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; // FreeMemory(offset); //MOD_DEC_USE_COUNT; PDEBUG("memalloc_vm_close:\n");}static struct vm_operations_struct memalloc_vm_ops = { open:memalloc_vm_open, close:memalloc_vm_close, nopage:memalloc_vm_nopage,};/* the device's mmap method. The VFS has kindly prepared the process's vm_area_struct for us, so we examine this to see what was requested. this code is adapted from drivers/char/bttv.c:do_bttv_mmap */static int memalloc_mmap(struct file *filp, struct vm_area_struct *vma){ int result; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long size = (unsigned long) (vma->vm_end - vma->vm_start); PDEBUG("memalloc_mmap: off = 0x%08lx size = %lu\n", offset, size); spin_lock(&mem_lock); result = MapBuffer(filp, vma); if(result == 0) { vma->vm_ops = &memalloc_vm_ops; /* open is not implicitly called when mmap is called */ memalloc_vm_open(vma); } spin_unlock(&mem_lock); return result;}static int memalloc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ int err = 0; PDEBUG("ioctl cmd 0x%08x\n", cmd); /* * extract the type and number bitfields, and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if(_IOC_TYPE(cmd) != MEMALLOC_IOC_MAGIC) return -ENOTTY; if(_IOC_NR(cmd) > MEMALLOC_IOC_MAXNR) return -ENOTTY; /* * the direction is a bitmask, and VERIFY_WRITE catches R/W * transfers. `Type' is user-oriented, while * access_ok is kernel-oriented, so the concept of "read" and * "write" is reversed */ if(_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd)); else if(_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd)); if(err) return -EFAULT; switch (cmd) { case MEMALLOC_IOCHARDRESET: /* * reset the counter to 1, to allow unloading in case * of problems. Use 1, not 0, because the invoking * process has the device open. */ /* while(MOD_IN_USE) MOD_DEC_USE_COUNT; MOD_INC_USE_COUNT; */ break; case MEMALLOC_IOCXGETBUFFER: { unsigned long size; int result; unsigned long busaddr = 0; spin_lock(&mem_lock); __get_user(size, (unsigned long *) arg); result = AllocMemory(&busaddr, size); __put_user((unsigned long) busaddr, (unsigned long *) arg); spin_unlock(&mem_lock); return result; break; } case MEMALLOC_IOCSFREEBUFFER: { unsigned long busaddr; int result; spin_lock(&mem_lock); __get_user(busaddr, (unsigned long *) arg); spin_unlock(&mem_lock); result = FreeMemory(busaddr); } } return 0;}static int memalloc_open(struct inode *inode, struct file *filp){#if 0 if(MOD_IN_USE) /* just single access */ return -EBUSY;#endif// MOD_INC_USE_COUNT; PDEBUG("dev opened\n"); return 0;}static int memalloc_release(struct inode *inode, struct file *filp){// MOD_DEC_USE_COUNT; PDEBUG("dev closed\n"); return 0;}/* VFS methods */static struct file_operations memalloc_fops = { .mmap=memalloc_mmap, .open=memalloc_open, .release=memalloc_release, .ioctl=memalloc_ioctl,};int __init memalloc_init(void){ int result; /* if you want to test the module, you obviously need to * "mknod". Note that normally you would use the misc_device * interface, but I'm too lazy to change it. */ printk("<1>memalloc module init\n"); result = register_chrdev(memalloc_major, "memalloc", &memalloc_fops); if(result < 0) { PDEBUG("unable to get major %d\n", memalloc_major); goto err; } INIT_LIST_HEAD(&heap_list); memalloc_cache = kmem_cache_create("memalloc", sizeof(struct allocation), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); printk(KERN_INFO "memalloc: module inserted,major:%d\n",memalloc_major); //EXPORT_NO_SYMBOLS; return 0; err: printk(KERN_INFO "memalloc: module not inserted\n"); unregister_chrdev(memalloc_major, "memalloc"); return result;}void __exit memalloc_cleanup(void){ struct list_head *ptr; struct allocation *entry; struct page *pg; PDEBUG("clenup called\n"); unregister_chrdev(memalloc_major, "memalloc"); while(!list_empty(&heap_list)) { ptr = heap_list.next; entry = list_entry(ptr, struct allocation, list); /* first unreserve */ for(pg = virt_to_page(entry->buffer); pg < (virt_to_page(entry->buffer) + (1 << entry->order)); pg++) { mem_map_unreserve(pg); } PDEBUG("Free buffer: base = 0x%08lx size = %lu\n", virt_to_phys(entry->buffer), PAGE_SIZE << entry->order); /* and now free the memory */ list_del(ptr); free_pages((long) entry->buffer, entry->order); kmem_cache_free(memalloc_cache, entry); } kmem_cache_destroy(memalloc_cache); printk(KERN_INFO "memalloc: module removed\n"); return;}//arch_initcall(memalloc_init);module_init(memalloc_init);module_exit(memalloc_cleanup);static int AllocMemory(unsigned long *busaddr, unsigned int size){ struct page *pg; void *start_addr, *end_addr; u32 order, sz; struct allocation *new; *busaddr = 0; for(order = 0, sz = PAGE_SIZE; sz < size; order++, sz <<= 1) ; if(size != PAGE_SIZE << order) { PDEBUG("Alloc size >= PAGE_SIZE & must be a power of 2\n"); return -EINVAL; } new = kmem_cache_alloc(memalloc_cache, GFP_KERNEL); if(!new) { printk(KERN_INFO "memalloc: failed to alloc memory\n"); return -ENOMEM; } /* alloc memory */ start_addr = (void *) __get_free_pages(GFP_KERNEL, order); if(!start_addr) { printk(KERN_INFO "memalloc: failed to alloc memory\n"); kmem_cache_free(memalloc_cache, new); return -ENOMEM; }// memset(start_addr, 0, size); /* clear the mem */// strcpy(start_addr, "Mapping test, data written from kernel!"); new->buffer = start_addr; new->order = order; list_add_tail(&new->list, &heap_list); end_addr = start_addr + (PAGE_SIZE << order) - 1; for(pg = virt_to_page(start_addr); pg <= virt_to_page(end_addr); pg++) { mem_map_reserve(pg); } *busaddr = virt_to_phys(start_addr); PDEBUG("Alloc buffer: base = 0x%08lx size = %lu\n", *busaddr, PAGE_SIZE << order); return 0;}static int FreeMemory(unsigned long busaddr){ struct page *pg; struct allocation *entry = NULL; entry = FindEntryByBus(busaddr); if(entry == NULL) { return -EINVAL; } /* first unreserve */ for(pg = virt_to_page(entry->buffer); pg < (virt_to_page(entry->buffer) + (1 << entry->order)); pg++) { mem_map_unreserve(pg); } /* and now free the memory */ list_del(&entry->list); free_pages((long) entry->buffer, entry->order); kmem_cache_free(memalloc_cache, entry); PDEBUG("Free buffer: base = 0x%08lx size = %lu\n", virt_to_phys(entry->buffer), PAGE_SIZE << entry->order); return 0;}static int MapBuffer(struct file *filp, struct vm_area_struct *vma){ unsigned long phys; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long start = (unsigned long) vma->vm_start; unsigned long size = (unsigned long) (vma->vm_end - vma->vm_start); struct allocation *entry; entry = FindEntryByBus(offset); if(entry == NULL) { PDEBUG("map buffer entry not found in list\n"); return -EINVAL; } /* if userspace tries to mmap beyond end of our buffer, fail */ if(size != (PAGE_SIZE << entry->order)) { PDEBUG("map buffer size does not match\n"); return -EINVAL; } vma->vm_flags |= VM_RESERVED; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Remember this won't work for vmalloc()d memory ! */ phys = virt_to_phys(entry->buffer); /* stiger: old if(remap_page_range(start, phys, size, vma->vm_page_prot)) */ //printk("<1>memalloc:remap_page_range,%x,%x,%x, :%x\n",start, phys , size, pgprot_val(vma->vm_page_prot) ); if(remap_pfn_range(vma, start, phys >> PAGE_SHIFT, size, vma->vm_page_prot))// if(remap_page_range(vma, start, phys , size, vma->vm_page_prot)) { return -EAGAIN; } return 0;}struct allocation *FindEntryByBus(unsigned long busaddr){ struct list_head *ptr; struct allocation *entry; list_for_each(ptr, &heap_list) { entry = list_entry(ptr, struct allocation, list); if(virt_to_phys(entry->buffer) == busaddr) { return entry; } } return NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -