/*********************************************************************/
/* Copyright 2009, 2010 The University of Texas at Austin. */
/* All rights reserved. */
/* */
/* Redistribution and use in source and binary forms, with or */
/* without modification, are permitted provided that the following */
/* conditions are met: */
/* */
/* 1. Redistributions of source code must retain the above */
/* copyright notice, this list of conditions and the following */
/* disclaimer. */
/* */
/* 2. Redistributions in binary form must reproduce the above */
/* copyright notice, this list of conditions and the following */
/* disclaimer in the documentation and/or other materials */
/* provided with the distribution. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF TEXAS AT */
/* AUSTIN ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, */
/* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
/* DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OF TEXAS AT */
/* AUSTIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, */
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
/* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE */
/* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR */
/* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF */
/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */
/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
/* POSSIBILITY OF SUCH DAMAGE. */
/* */
/* The views and conclusions contained in the software and */
/* documentation are those of the authors and should not be */
/* interpreted as representing official policies, either expressed */
/* or implied, of The University of Texas at Austin. */
/*********************************************************************/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/pgtable.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#ifdef CONFIG_BIGPHYS_AREA
#include <linux/bigphysarea.h>
#endif
#include <asm/current.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <asm/io.h>
typedef struct {
pid_t pid;
#ifndef CONFIG_BIGPHYS_AREA
long size;
#endif
caddr_t address;
} buffer_t;
#define MAX_BUFF_SIZE 1024
#define MAX_LENGTH (4UL << 20)
static spinlock_t lock __attribute__((aligned(64)));
static buffer_t buffer[MAX_BUFF_SIZE];
static dev_t mapper_dev;
static struct cdev mapper_cdev;
static int mapper_open (struct inode *inode, struct file *fp){ return 0;}
static int mapper_release(struct inode *inode, struct file *fp){
int pos;
#ifndef CONFIG_BIGPHYS_AREA
caddr_t addr;
#endif
// printk("Releasing memory... %d\n", current -> tgid);
spin_lock(&lock);
for (pos = 0; pos < MAX_BUFF_SIZE; pos ++) {
if (buffer[pos].pid == (pid_t) current -> tgid) {
#ifdef CONFIG_BIGPHYS_AREA
bigphysarea_free_pages(buffer[pos].address);
#else
for (addr = buffer[pos].address; addr < buffer[pos].address + buffer[pos].size; addr += PAGE_SIZE) {
ClearPageReserved(virt_to_page(addr));
}
kfree(buffer[pos].address);
buffer[pos].size = 0;
#endif
buffer[pos].pid = 0;
buffer[pos].address = 0;
}
}
spin_unlock(&lock);
return 0;
}
int mapper_mapper(struct file *fp, struct vm_area_struct *vma){
int ret, pos;
caddr_t alloc_addr;
#ifndef CONFIG_BIGPHYS_AREA
caddr_t addr;
#endif
long all_length, length, current_addr;
all_length = vma->vm_end - vma->vm_start;
current_addr = vma -> vm_start;
spin_lock(&lock);
while (all_length > 0) {
length = all_length;
if (length > MAX_LENGTH) length = MAX_LENGTH;
all_length -= MAX_LENGTH;
// printk("Allocating memory... %d\n", length);
pos = 0;
while ((pos < MAX_BUFF_SIZE) && (buffer[pos].address != 0)) pos ++;
if (pos >= MAX_BUFF_SIZE) {
printk("Memory Allocator : too much memory allocation requested.\n");
spin_unlock(&lock);
return -EIO;
}
#ifdef CONFIG_BIGPHYS_AREA
alloc_addr = (caddr_t)bigphysarea_alloc_pages(length >> PAGE_SHIFT, 1, GFP_KERNEL);
#else
alloc_addr = (caddr_t)kmalloc(length, GFP_KERNEL);
#endif
if (alloc_addr == (caddr_t)NULL) {
spin_unlock(&lock);
return -EIO;
}
#ifndef CONFIG_BIGPHYS_AREA
for (addr = alloc_addr; addr < alloc_addr + length; addr += PAGE_SIZE) {
clear_page(addr);
SetPageReserved(virt_to_page(addr));
}
#endif
if ((ret = remap_pfn_range(vma,
current_addr,
virt_to_phys((void *)alloc_addr) >> PAGE_SHIFT,
length,
PAGE_SHARED)) < 0) {
#ifdef CONFIG_BIGPHYS_AREA
bigphysarea_free_pages((caddr_t)alloc_addr);
#else
for (addr = alloc_addr; addr < alloc_addr + length; addr += PAGE_SIZE) ClearPageReserved(virt_to_page(addr));
kfree((caddr_t)alloc_addr);
#endif
spin_unlock(&lock);
return ret;
}
buffer[pos].pid = current -> tgid;
buffer[pos].address = alloc_addr;
#ifndef CONFIG_BIGPHYS_AREA
buffer[pos].size = length;
#endif
current_addr += length;
}
spin_unlock(&lock);
return 0;
}
static struct file_operations mapper_fops = {
.open = mapper_open,
.release = mapper_release,
.mmap = mapper_mapper,
.owner = THIS_MODULE,
};
static int __init mapper_init(void){
int ret, i;
ret = alloc_chrdev_region(&mapper_dev, 0, 1, "mapper");
cdev_init(&mapper_cdev, &mapper_fops);
ret = cdev_add(&mapper_cdev, mapper_dev, 1);
spin_lock_init(&lock);
for (i = 0; i < MAX_BUFF_SIZE; i++) {
buffer[i].pid = 0;
#ifndef CONFIG_BIGPHYS_AREA
buffer[i].size = 0;
#endif
buffer[i].address = 0;
}
return ret;
}
static void __exit mapper_exit(void){
int pos;
for (pos = 0; pos < MAX_BUFF_SIZE; pos ++) {
if (buffer[pos].address != 0) {
#ifdef CONFIG_BIGPHYS_AREA
bigphysarea_free_pages(buffer[pos].address);
#else
kfree(buffer[pos].address);
#endif
}
}
cdev_del(&mapper_cdev);
unregister_chrdev_region(mapper_dev, 1);
}
module_init(mapper_init);
module_exit(mapper_exit);
MODULE_DESCRIPTION("BigPhysArea User Mapping Driver");
MODULE_LICENSE("Unknown");