amifldrv/amifldrv.c

342 lines
7.6 KiB
C
Raw Normal View History

2016-09-27 01:54:42 +00:00
/*
* American Megatrends ROM Driver
* There wasn't a copyright here, so idk
*/
2015-08-30 15:47:29 +00:00
#include <asm/io.h>
2016-09-27 01:54:42 +00:00
#include <linux/version.h>
#include <linux/mm.h>
2015-08-30 15:47:29 +00:00
#include <linux/interrupt.h>
#include <linux/module.h>
2016-09-27 01:54:42 +00:00
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/fs.h>
2016-09-27 04:01:34 +00:00
#include <linux/version.h>
#define LINUX_PRE_2_6 (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
#define LINUX_POST_2_6 (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
#if LINUX_PRE_2_6
2018-07-24 13:29:28 +00:00
#include <linux/wrapper.h>
#else
#define mem_map_reserve(p) set_bit (PG_reserved, &((p)->flags))
#define mem_map_unreserve(p) clear_bit(PG_reserved, &((p)->flags))
#endif
2016-09-27 01:54:42 +00:00
2016-09-27 04:08:02 +00:00
#define CMD_ALLOC 0x4160
#define CMD_FREE 0x4161
#define CMD_LOCK_KB 0x4162
#define CMD_UNLOCK_KB 0x4163
2016-09-27 04:01:34 +00:00
/*
* ioctl data packet used to communicate instructions to the driver
*/
typedef struct _struct_AMIFL_alloc_params {
2018-07-24 13:29:28 +00:00
long size;
unsigned long kvirtlen;
void *kmallocptr;
void *kvirtadd;
void *kphysadd;
2016-09-27 04:01:34 +00:00
} AMIFL_alloc_params;
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
static int *kmalloc_area = NULL;
static int *kmalloc_ptr = NULL;
2015-08-30 15:47:29 +00:00
static unsigned long kmalloc_len = 0L;
2018-07-24 13:29:28 +00:00
static int kcount = 0;
2015-08-30 15:47:29 +00:00
static int major;
2016-09-27 04:01:34 +00:00
static AMIFL_alloc_params kmalloc_drv[128];
2016-09-27 01:54:42 +00:00
/*
* Section: Character Device Implementation
* ============================================================================
*/
#if LINUX_POST_2_6
static int chardev_open_count = 0;
2018-07-24 13:29:28 +00:00
#endif
2016-09-27 01:54:42 +00:00
/**
* Character Device open action
*/
2018-07-24 13:29:28 +00:00
int AMI_chrdrv_open(struct inode *inode, struct file *file)
2015-08-30 15:47:29 +00:00
{
2016-09-27 01:54:42 +00:00
#if LINUX_PRE_2_6
2018-07-24 13:29:28 +00:00
MOD_INC_USE_COUNT;
2016-09-27 01:54:42 +00:00
#else
2018-07-24 13:29:28 +00:00
if (chardev_open_count > 0) {
return -EBUSY;
}
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
++chardev_open_count;
try_module_get(THIS_MODULE);
2016-09-27 01:54:42 +00:00
#endif
2018-07-24 13:29:28 +00:00
return (0);
2015-08-30 15:47:29 +00:00
}
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
int AMI_chrdrv_release(struct inode *inode, struct file *file)
2015-08-30 15:47:29 +00:00
{
2016-09-27 01:54:42 +00:00
#if LINUX_PRE_2_6
2018-07-24 13:29:28 +00:00
MOD_DEC_USE_COUNT;
#else
--chardev_open_count;
module_put(THIS_MODULE);
#endif
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
return (0);
2015-08-30 15:47:29 +00:00
}
2016-09-27 01:54:42 +00:00
#if defined(HAVE_UNLOCKED_IOCTL)
2018-07-24 13:29:28 +00:00
long AMI_chrdrv_ioctl(struct file *_unused_file, unsigned int cmd, unsigned long arg)
2016-09-27 01:54:42 +00:00
#else
2018-07-24 13:29:28 +00:00
int AMI_chrdrv_ioctl(struct inode *_unused_inode, unsigned int cmd, unsigned long arg)
#endif
2015-08-30 15:47:29 +00:00
{
2018-07-24 13:29:28 +00:00
switch (cmd) {
case CMD_ALLOC:
{
unsigned long virt_addr;
AMIFL_alloc_params arg_kernel_space;
if (kcount >= 128) {
return -EINVAL;
}
kmalloc_ptr = NULL;
if (!arg || kmalloc_ptr) {
return -EINVAL;
}
copy_from_user((void *) &arg_kernel_space, (void *) arg,
sizeof(AMIFL_alloc_params));
if (arg_kernel_space.size > 128 * 1024) {
return -EINVAL;
}
kmalloc_len = ((arg_kernel_space.size + PAGE_SIZE - 1) & PAGE_MASK);
kmalloc_ptr = kmalloc((kmalloc_len + 2 * PAGE_SIZE), GFP_DMA | GFP_KERNEL);
kmalloc_area =
(int *) (((unsigned long) kmalloc_ptr + PAGE_SIZE - 1) & PAGE_MASK);
for (virt_addr = (unsigned long) kmalloc_area;
virt_addr < (unsigned long) kmalloc_area + kmalloc_len;
virt_addr += PAGE_SIZE) {
mem_map_reserve(virt_to_page(virt_addr));
}
{
int i;
for (i = 0; i < (kmalloc_len / sizeof(int)); ++i) {
kmalloc_area[i] = 0xAFD00000 + i;
}
}
kmalloc_drv[kcount].size = arg_kernel_space.size;
kmalloc_drv[kcount].kmallocptr = kmalloc_ptr;
kmalloc_drv[kcount].kvirtlen = kmalloc_len;
kmalloc_drv[kcount].kvirtadd = kmalloc_area;
kmalloc_drv[kcount].kphysadd =
(void *) ((unsigned long) virt_to_phys(kmalloc_area));
++kcount;
arg_kernel_space.kvirtadd = kmalloc_area;
arg_kernel_space.kphysadd =
(void *) ((unsigned long) virt_to_phys(kmalloc_area));
copy_to_user((void *) arg, (void *) &arg_kernel_space,
sizeof(AMIFL_alloc_params));
return 0;
}
case CMD_FREE:
{
unsigned long virt_addr;
AMIFL_alloc_params arg_kernel_space;
int isearch = 0;
copy_from_user((void *) &arg_kernel_space, (void *) arg,
sizeof(AMIFL_alloc_params));
if (kcount > 0) {
for (isearch = 0; isearch < kcount; isearch++) {
if (kmalloc_drv[isearch].kphysadd ==
arg_kernel_space.kphysadd)
break;
}
if (isearch >= kcount)
return 0;
kmalloc_ptr = kmalloc_drv[isearch].kmallocptr;
kmalloc_area = kmalloc_drv[isearch].kvirtadd;
kmalloc_len = kmalloc_drv[isearch].kvirtlen;
} else
return 0;
if (kmalloc_ptr) {
for (virt_addr = (unsigned long) kmalloc_area;
virt_addr < (unsigned long) kmalloc_area + kmalloc_len;
virt_addr += PAGE_SIZE) {
mem_map_unreserve(virt_to_page(virt_addr));
}
if (kmalloc_ptr) {
kfree(kmalloc_ptr);
}
kmalloc_len = 0L;
kmalloc_ptr = NULL;
kmalloc_area = NULL;
kcount--;
if (isearch != kcount) {
kmalloc_drv[isearch].size = kmalloc_drv[kcount].size;
kmalloc_drv[isearch].kmallocptr =
kmalloc_drv[kcount].kmallocptr;
kmalloc_drv[isearch].kvirtlen =
kmalloc_drv[kcount].kvirtlen;
kmalloc_drv[isearch].kvirtadd =
kmalloc_drv[kcount].kvirtadd;
kmalloc_drv[isearch].kphysadd =
kmalloc_drv[kcount].kphysadd;
}
kmalloc_drv[kcount].size = 0;
kmalloc_drv[kcount].kmallocptr = NULL;
kmalloc_drv[kcount].kvirtlen = 0;
kmalloc_drv[kcount].kvirtadd = NULL;
kmalloc_drv[kcount].kphysadd = NULL;
}
return 0;
}
case CMD_LOCK_KB:
disable_irq(1);
return 0;
case CMD_UNLOCK_KB:
enable_irq(1);
return 0;
}
return -ENOTTY;
2015-08-30 15:47:29 +00:00
}
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
int AMI_chrdrv_mmap(struct file *file, struct vm_area_struct *vma)
2015-08-30 15:47:29 +00:00
{
2018-07-24 13:29:28 +00:00
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long size = vma->vm_end - vma->vm_start;
if (offset & ~PAGE_MASK) {
return -ENXIO;
}
if (!kmalloc_ptr) {
return (-ENXIO);
}
if (size > kmalloc_len) {
return (-ENXIO);
}
if ((offset + size) > kmalloc_len) {
return -ENXIO;
}
if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
return (-EINVAL);
}
vma->vm_flags |= VM_LOCKED;
{
unsigned long pfn = virt_to_phys((void *) ((unsigned long) kmalloc_area));
2016-09-27 01:54:42 +00:00
#if LINUX_POST_2_6
2018-07-24 13:29:28 +00:00
int remap_result = remap_pfn_range(vma,
vma->vm_start,
pfn >> PAGE_SHIFT,
size,
PAGE_SHARED);
2016-09-27 01:54:42 +00:00
#else
2018-07-24 13:29:28 +00:00
int remap_result = remap_page_range(vma,
vma->vm_start,
pfn,
size,
PAGE_SHARED);
2016-09-27 01:54:42 +00:00
#endif
2018-07-24 13:29:28 +00:00
if (remap_result) {
return -ENXIO;
}
}
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
return (0);
2016-09-27 01:54:42 +00:00
}
#if defined(HAVE_UNLOCKED_IOCTL)
2018-07-24 13:29:28 +00:00
#define $IOCTL_FIELD unlocked_ioctl
2016-09-27 01:54:42 +00:00
#else
2018-07-24 13:29:28 +00:00
#define $IOCTL_FIELD ioctl
#endif
2016-09-27 01:54:42 +00:00
struct file_operations AMI_chrdrv_fops = {
2018-07-24 13:29:28 +00:00
owner:THIS_MODULE,
open:AMI_chrdrv_open,
release:AMI_chrdrv_release,
mmap:AMI_chrdrv_mmap,
$IOCTL_FIELD:AMI_chrdrv_ioctl,
2016-09-27 01:54:42 +00:00
};
/*
* Section: Linux Kernel Module Setup
* ============================================================================
*/
#if LINUX_POST_2_6
2018-07-24 13:29:28 +00:00
MODULE_AUTHOR("American Megatrends Inc.");
MODULE_DESCRIPTION("AMI Flash Update utility driver");
MODULE_LICENSE("Proprietary");
2016-09-27 01:54:42 +00:00
#endif
2018-07-24 13:29:28 +00:00
static int /* module_init */ amifldrv_init_module(void)
2016-09-27 01:54:42 +00:00
{
2018-07-24 13:29:28 +00:00
major = register_chrdev(0, "amifldrv", &AMI_chrdrv_fops);
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
if (major < 0) {
return -EIO;
}
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
memset(kmalloc_drv, 0, sizeof(AMIFL_alloc_params) * 128);
return (0);
2015-08-30 15:47:29 +00:00
}
2016-09-27 01:54:42 +00:00
2018-07-24 13:29:28 +00:00
static void /* module_exit */ amifldrv_cleanup_module(void)
2016-09-27 01:54:42 +00:00
{
2018-07-24 13:29:28 +00:00
unsigned long virt_addr;
if (kcount > 0) {
{
int iloop;
for (iloop = 0; iloop < kcount; ++iloop) {
kmalloc_ptr = kmalloc_drv[iloop].kmallocptr;
kmalloc_area = kmalloc_drv[iloop].kvirtadd;
kmalloc_len = kmalloc_drv[iloop].kvirtlen;
if (kmalloc_ptr) {
for (virt_addr = (unsigned long) kmalloc_area;
virt_addr < (unsigned long) kmalloc_area + kmalloc_len;
virt_addr += PAGE_SIZE) {
mem_map_unreserve(virt_to_page(virt_addr));
}
if (kmalloc_ptr) {
kfree(kmalloc_ptr);
}
}
}
}
kcount = 0;
}
unregister_chrdev(major, "amifldrv");
return;
2016-09-27 01:54:42 +00:00
}
module_init(amifldrv_init_module);
module_exit(amifldrv_cleanup_module);