This commit is contained in:
Tom Reynolds 2019-04-12 23:27:37 +02:00
parent a205a83ae5
commit 3f5e410e71
6 changed files with 1262 additions and 0 deletions

16
ia32/Makefile Normal file
View File

@ -0,0 +1,16 @@
#
# Makefile for InsydeFlash tool for Linux (build your main kernel)
#
MACHINE = $(shell uname -m)
obj-m+=isfl_drv.o
all: isfl_drv
isfl_drv:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

57
ia32/isfl.h Normal file
View File

@ -0,0 +1,57 @@
/*
* InsydeFlash Linux Driver
* File: isfl.h
*
* Copyright (C) 2005 - 2015 Insyde Software Corp.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
#define DEVICE_NAME "isfl" //InsydeFlash buffer
#define DEVICE_NAME_PATH "/dev/"DEVICE_NAME // Open device node on user-space
#define DEVICE_MAJOR_NUM 100
#define ISFB_FLASH_SIZE ( 128 * 1024 ) // 128KB
#define MEMORY_BLOCK_SIZE ( 64 * 1024 ) // 64KB
#define MAX_MEMORY_BLOCK_COUNT 512
struct _isfl
{
unsigned long smi_io; // SMI Port
unsigned long mbuf_size; // memory buffer size
unsigned long mbuf_paddr; // memory buffer physical address
unsigned long smi_status; // SW SMI Status Return
unsigned long enable_kbc;
unsigned long disable_kbc;
};
struct _mblock
{
unsigned long mbuf_PhyAddr; // memory buffer index
unsigned long mbuf_size; // memory buffer size
unsigned char pbyBuffer[MEMORY_BLOCK_SIZE];
};
#define IOCTL_ALLOCATE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 0, struct _isfl *)
#define IOCTL_FREE_MEMORY _IOW(DEVICE_MAJOR_NUM, 1, struct _isfl *)
#define IOCTL_WRITE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 2, struct _isfl *)
#define IOCTL_READ_MEMORY _IOWR(DEVICE_MAJOR_NUM, 3, struct _isfl *)
#define IOCTL_GENERATE_SMI _IOW(DEVICE_MAJOR_NUM, 4, struct _isfl *)
#define IOCTL_GET_SMI_STATUS _IOR(DEVICE_MAJOR_NUM, 5, struct _isfl *)
#define IOCTL_DISABLE_KBC _IOR(DEVICE_MAJOR_NUM, 6, struct _isfl *)
#define IOCTL_ENABLE_KBC _IOR(DEVICE_MAJOR_NUM, 7, struct _isfl *)
#define IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY _IOWR(DEVICE_MAJOR_NUM, 8, struct _isfl *)
#define IOCTL_FREE_MULTI_BLOCK_MEMORY _IOW(DEVICE_MAJOR_NUM, 9, struct _isfl *)
#define IOCTL_WRITE_TO_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 10, struct _mblock *)
#define IOCTL_READ_FROME_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 11, struct _mblock *)
#endif

558
ia32/isfl_drv.c Normal file
View File

@ -0,0 +1,558 @@
/*
* InsydeFlash Linux Driver
* File: isfl_drv.c
*
* Copyright (C) 2005 - 2015 Insyde Software Corp.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/io.h>
typedef struct _PageAlloc {
unsigned long addr;
unsigned long PhyAddr;
unsigned int order;
}PageAlloc_t;
#include "isfl.h"
#define MAX_8042_LOOPS 100000
#define ISFL_VERSION "0.0.05"
#define DRIVER_AUTHOR "Howard Ho <howard.ho@insydesw.com.tw>"
#define DRIVER_DESC "InsydeFlash tool for Linux"
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
static dev_t first;
static struct cdev c_dev;
static struct class *isfl_class;
#endif
static int Device_Open = 0;
static unsigned char *isfl_mem_buff;
static unsigned char *g_ppbyMemoryBuffer[MAX_MEMORY_BLOCK_COUNT] = {0};
static PageAlloc_t g_ppbyMemoryBuffer2[MAX_MEMORY_BLOCK_COUNT] = {{0,0}};
static int isfl_open(struct inode *, struct file *);
static int isfl_release(struct inode *, struct file *);
static ssize_t isfl_read(struct file *, char *, size_t, loff_t *);
static ssize_t isfl_write(struct file *file, const char *buff, size_t length, loff_t *offset);
//Flash will close kbc except show dialog
static void enable_a20_kbc(void);
static void disable_a20_kbc(void);
//static int empty_8042(void);
static inline void io_delay(void);
//Use this function to get the message from AP
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
static long isfl_ioctl(struct file *file, unsigned int num, unsigned long arg)
#else
static int isfl_ioctl(struct inode *inode, struct file *file, unsigned int num, unsigned long arg)
#endif
{
unsigned long isfl_phys_addr; // Assgin to SW SMI
struct _isfl *isfl;
int isfl_size = sizeof( struct _isfl );
unsigned char buf[isfl_size];
struct _mblock *pBlock;
int iBlockStructureSize = sizeof( struct _mblock );
unsigned char *pbyBlockBuffer;
unsigned char *ch;
int i;
unsigned long mbuf_index;
unsigned char *vir_addr = 0;
switch ( num )
{
case IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY:
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
}
isfl = (struct _isfl *)buf;
mbuf_index = isfl->mbuf_paddr; // use mbuf_paddr to store mbuf_index to backward compatible
// Allocate memory buffer
g_ppbyMemoryBuffer2[mbuf_index].order = get_order( isfl->mbuf_size );
g_ppbyMemoryBuffer2[mbuf_index].addr = __get_free_pages( GFP_DMA32, g_ppbyMemoryBuffer2[mbuf_index].order );
g_ppbyMemoryBuffer[mbuf_index] = (unsigned char *)g_ppbyMemoryBuffer2[mbuf_index].addr;
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY allocate physical memory failed\n" );
return -1;
}
memset( g_ppbyMemoryBuffer[mbuf_index], 0, isfl->mbuf_size );
g_ppbyMemoryBuffer2[mbuf_index].PhyAddr = virt_to_phys( g_ppbyMemoryBuffer[mbuf_index] );
isfl->mbuf_paddr = g_ppbyMemoryBuffer2[mbuf_index].PhyAddr;
//printk( KERN_INFO "isfl alloc id=%d, PhyAddr=%x\n", mbuf_index, g_ppbyMemoryBuffer2[mbuf_index].PhyAddr );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
put_user( buf[i], ( ch + i ) );
}
break;
case IOCTL_FREE_MULTI_BLOCK_MEMORY:
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
}
isfl = (struct _isfl *)buf;
mbuf_index = -1;
isfl_phys_addr = isfl->mbuf_paddr;
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
mbuf_index = i;
if (-1 == mbuf_index)
return -1;
// Free Memory
if ( g_ppbyMemoryBuffer[mbuf_index] != NULL )
{
free_pages( g_ppbyMemoryBuffer2[mbuf_index].addr, g_ppbyMemoryBuffer2[mbuf_index].order );
g_ppbyMemoryBuffer2[mbuf_index].addr = 0;
g_ppbyMemoryBuffer2[mbuf_index].order = 0;
g_ppbyMemoryBuffer[mbuf_index] = NULL;
}
break;
case IOCTL_WRITE_TO_PHYSICAL:
ch = (unsigned char *)arg;
pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA );
if ( pbyBlockBuffer == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with NULL parameter\n" );
return -1;
}
for ( i = 0; i < iBlockStructureSize; i++ )
{
get_user( pbyBlockBuffer[i], ( ch + i ) );
}
pBlock = (struct _mblock *)pbyBlockBuffer;
isfl_phys_addr = pBlock->mbuf_PhyAddr;
//printk( KERN_INFO "isfl write PhyAddr=%x\n", pBlock->mbuf_PhyAddr);
//printk( KERN_INFO "isfl write len=%x\n", pBlock->mbuf_size);
//printk( KERN_INFO "isfl write PhyAddr=%x\n", isfl_phys_addr );
mbuf_index = -1;
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
mbuf_index = i;
if (-1 == mbuf_index)
{
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with wrong physcial address\n" );
return -1;
}
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with physcial address didn't allocate\n" );
return -1;
}
for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ )
{
g_ppbyMemoryBuffer[mbuf_index][i] = pBlock->pbyBuffer[i];
}
kfree( pbyBlockBuffer );
break;
case IOCTL_READ_FROME_PHYSICAL:
ch = (unsigned char *)arg;
pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA );
if ( pbyBlockBuffer == NULL )
{
return -1;
}
for ( i = 0; i < iBlockStructureSize; i++ )
{
get_user( pbyBlockBuffer[i], ( ch + i ) );
}
pBlock = (struct _mblock *)pbyBlockBuffer;
isfl_phys_addr = pBlock->mbuf_PhyAddr;
//printk( KERN_INFO "isfl read PhyAddr=%x\n", pBlock->mbuf_PhyAddr);
//printk( KERN_INFO "isfl read len=%x\n", pBlock->mbuf_size);
//printk( KERN_INFO "isfl read PhyAddr=%x\n", isfl_phys_addr );
mbuf_index = -1;
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
mbuf_index = i;
if (-1 == mbuf_index)
return -1;
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
{
return -1;
}
for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ )
{
pBlock->pbyBuffer[i] = g_ppbyMemoryBuffer[mbuf_index][i];
}
for ( i = 0; i < iBlockStructureSize; i++ )
{
put_user( pbyBlockBuffer[i], ( ch + i ) );
}
kfree( pbyBlockBuffer );
break;
case IOCTL_ALLOCATE_MEMORY:
//printk( KERN_INFO "IOCTL_ALLOCATE_MEMORY\n" );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
//printk( KERN_INFO "%02X ", buf[i] );
}
//printk( KERN_INFO "\n" );
isfl = (struct _isfl *)buf;
/* Allocate a memory buffer */
//printk( KERN_INFO "Allocate Memory size: %ld\n", isfl->mbuf_size );
isfl_mem_buff = kmalloc( isfl->mbuf_size, GFP_DMA ); //20090605 - Keep OS allocate under 16M
if ( isfl_mem_buff == NULL )
{
return -1;
}
memset( isfl_mem_buff, 0, isfl->mbuf_size );
isfl_phys_addr = virt_to_phys( isfl_mem_buff );
//printk( KERN_INFO "isfl: virt: 0x%p phys: 0x%08lx\n", isfl_mem_buff, isfl_phys_addr );
isfl->mbuf_paddr = isfl_phys_addr;
//printk( KERN_INFO "Memory physical addr: 0x%08lx\n", isfl->mbuf_paddr );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
//printk( KERN_INFO "%02X ", buf[i] );
put_user( buf[i], ( ch + i ) );
}
//printk( KERN_INFO "\n" );
break;
case IOCTL_FREE_MEMORY:
//printk( KERN_INFO "IOCTL_FREE_MEMORY\n" );
//kfree( isfl_mem_buff );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
//printk( KERN_INFO "%02X ", buf[i] );
}
isfl = (struct _isfl *)buf;
isfl_phys_addr = isfl->mbuf_paddr;
vir_addr = (unsigned char*)phys_to_virt (isfl_phys_addr);
if ( vir_addr == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_READ_FROME_PHYSICAL with failure from phys_to_virt\n" );
return -1;
}
kfree (vir_addr);
break;
case IOCTL_GENERATE_SMI:
//printk( "IOCTL_GENERATE_SMI\n" );
//printk( "size of (struct _isfl): %d\n", isfl_size );
/* Get SMI initial data */
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
//printk( "%02X ", buf[i] );
}
//printk( "\n" );
isfl = (struct _isfl *)buf;
//printk( "SMI IO port: 0x%lX\n", isfl->smi_io );
//printk( "Bufer Size: %ld\n", isfl->mbuf_size );
break;
case IOCTL_GET_SMI_STATUS:
//printk( "IOCTL_GET_SMI_STATUS\n" );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
//printk( "%02X ", buf[i] );
put_user( buf[i], ( ch + i ) );
}
//printk( "\n" );
break;
case IOCTL_ENABLE_KBC:
enable_a20_kbc();
break;
case IOCTL_DISABLE_KBC:
disable_a20_kbc();
break;
}
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
static DEFINE_MUTEX( isflash_mutex );
static long isfl_ioctl_unlock(struct file *fp, unsigned int cmd, unsigned long arg)
{
long ret;
mutex_lock( &isflash_mutex );
ret = isfl_ioctl( fp, cmd, arg );
mutex_unlock( &isflash_mutex );
return ret;
}
#endif
static struct file_operations fops = {
.read = isfl_read,
.write = isfl_write,
.open = isfl_open,
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
.unlocked_ioctl = isfl_ioctl_unlock,
.compat_ioctl = isfl_ioctl_unlock,
#else
.ioctl = isfl_ioctl,
#endif
.release = isfl_release
};
//Driver initialization
static int __init init_isfl(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
first = MKDEV(DEVICE_MAJOR_NUM, 0);
if (register_chrdev_region(first, 1, DEVICE_NAME) < 0)
{
return -1;
}
if ((isfl_class = class_create(THIS_MODULE, DEVICE_NAME)) == NULL)
{
unregister_chrdev_region(first, 1);
return -1;
}
if (device_create(isfl_class, NULL, first, NULL, "isfl") == NULL)
{
class_destroy(isfl_class);
unregister_chrdev_region(first, 1);
return -1;
}
cdev_init(&c_dev, &fops);
if (cdev_add(&c_dev, first, 1) == -1)
{
device_destroy(isfl_class, first);
class_destroy(isfl_class);
unregister_chrdev_region(first, 1);
return -1;
}
#else
int ret = register_chrdev(DEVICE_MAJOR_NUM, DEVICE_NAME, &fops);
if ( ret < 0 )
{
//printk( KERN_ALERT "Registering char device %s failed with %d\n", DEVICE_NAME, ret );
return ret;
}
#endif
//printk ("<0> IOCTL_ALLOCATE_MEMORY = %lx\n", IOCTL_ALLOCATE_MEMORY );
//printk ("<0> IOCTL_FREE_MEMORY = %lx\n", IOCTL_FREE_MEMORY );
//printk ("<0> IOCTL_WRITE_MEMORY = %lx\n", IOCTL_WRITE_MEMORY );
//printk ("<0> IOCTL_READ_MEMORY = %lx\n", IOCTL_READ_MEMORY );
//printk ("<0> IOCTL_GENERATE_SMI = %lx\n", IOCTL_GENERATE_SMI );
//printk ("<0> IOCTL_GET_SMI_STATUS = %lx\n", IOCTL_GET_SMI_STATUS );
//printk ("<0> IOCTL_DISABLE_KBC = %lx\n", IOCTL_DISABLE_KBC );
//printk ("<0> IOCTL_ENABLE_KBC = %lx\n", IOCTL_ENABLE_KBC );
//printk ("<0> IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY);
//printk ("<0> IOCTL_FREE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_FREE_MULTI_BLOCK_MEMORY );
//printk ("<0> IOCTL_COPY_BUFFER_TO_MEMORY = %lx\n", IOCTL_COPY_BUFFER_TO_MEMORY );
//printk ("<0> = %d\n", MAJOR(first));
return 0;
}
static void __exit cleanup_isfl(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
cdev_del(&c_dev);
device_destroy(isfl_class, first);
class_destroy(isfl_class);
unregister_chrdev_region(first, 1);
#else
unregister_chrdev( DEVICE_MAJOR_NUM, DEVICE_NAME );
#endif
}
static int isfl_open(struct inode *inode, struct file *file)
{
if ( Device_Open )
{
return -EBUSY;
}
Device_Open++;
try_module_get( THIS_MODULE );
return 0;
}
static int isfl_release(struct inode *inode, struct file *file)
{
Device_Open--;
module_put( THIS_MODULE );
return 0;
}
static ssize_t isfl_read(struct file *filp, char *buff, size_t len, loff_t *offset)
{
int bytes_read = 0;
//printk( KERN_INFO "isfl_read\n" );
if ( isfl_mem_buff == 0 )
{
//printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" );
return -EFAULT;
}
while ( len )
{
put_user( isfl_mem_buff[bytes_read++], buff++ );
len--;
}
//printk( KERN_INFO "isfl_read: read %d bytes, left %d\n", bytes_read, len );
return bytes_read;
}
static ssize_t isfl_write(struct file *file, const char *buff, size_t len, loff_t *offset)
{
int i;
//printk( KERN_INFO "isfl_write\n" );
if ( isfl_mem_buff == 0 )
{
//printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" );
return -EFAULT;
}
//printk( KERN_INFO "isfl_write: len: %d\n", len );
for ( i = 0; i < len && i < ISFB_FLASH_SIZE; i++ )
{
get_user( *( isfl_mem_buff + i ), buff + i );
}
#if 0
for ( i = 0; i < len; i++ )
{
if ( ( i + 1 ) % 8 )
{
//printk( "%02x ", isfl_mem_buff[i] );
}
else if ( ( i + 1 ) %16 )
{
//printk( "%02x ", isfl_mem_buff[i] );
}
else
{
//printk( "%02x\n", isfl_mem_buff[i] );
}
}
//printk( "\n" );
#endif
return i;
}
static int empty_8042(void)
{
u8 status;
int loops = MAX_8042_LOOPS;
while ( loops-- )
{
io_delay();
status = inb( 0x64 );
if ( status & 1 )
{
/* Read and discard input data */
io_delay();
(void)inb( 0x60 );
}
else if ( !( status & 2 ) )
{
/* Buffers empty, finished! */
return 0;
}
}
return -1;
}
static void enable_a20_kbc(void)
{
empty_8042();
outb( 0x60, 0x64 ); // Command write
empty_8042();
outb( 0x47, 0x60 ); // A20 on
empty_8042();
}
static void disable_a20_kbc(void)
{
empty_8042();
outb( 0x60, 0x64 ); // Command write
empty_8042();
outb( 0x74, 0x60 ); // A20 on
empty_8042();
}
static inline void io_delay(void)
{
const u16 DELAY_PORT = 0x80;
asm volatile( "outb %%al,%0" : : "dN" (DELAY_PORT) );
}
module_init( init_isfl );
module_exit( cleanup_isfl );
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_VERSION( ISFL_VERSION );
MODULE_SUPPORTED_DEVICE( "Insyde EFI BIOS Flash" );
MODULE_LICENSE( "GPL" );

16
x64/Makefile Normal file
View File

@ -0,0 +1,16 @@
#
# Makefile for InsydeFlash tool for Linux (build your main kernel)
#
MACHINE = $(shell uname -m)
obj-m+=isfl_drv.o
all: isfl_drv
isfl_drv:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

57
x64/isfl.h Normal file
View File

@ -0,0 +1,57 @@
/*
* InsydeFlash Linux Driver
* File: isfl.h
*
* Copyright (C) 2005 - 2015 Insyde Software Corp.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
#define DEVICE_NAME "isfl" //InsydeFlash buffer
#define DEVICE_NAME_PATH "/dev/"DEVICE_NAME // Open device node on user-space
#define DEVICE_MAJOR_NUM 100
#define ISFB_FLASH_SIZE ( 128 * 1024 ) // 128KB
#define MEMORY_BLOCK_SIZE ( 64 * 1024 ) // 64KB
#define MAX_MEMORY_BLOCK_COUNT 512
struct _isfl
{
unsigned long smi_io; // SMI Port
unsigned long mbuf_size; // memory buffer size
unsigned long mbuf_paddr; // memory buffer physical address
unsigned long smi_status; // SW SMI Status Return
unsigned long enable_kbc;
unsigned long disable_kbc;
};
struct _mblock
{
unsigned long mbuf_PhyAddr; // memory buffer index
unsigned long mbuf_size; // memory buffer size
unsigned char pbyBuffer[MEMORY_BLOCK_SIZE];
};
#define IOCTL_ALLOCATE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 0, struct _isfl *)
#define IOCTL_FREE_MEMORY _IOW(DEVICE_MAJOR_NUM, 1, struct _isfl *)
#define IOCTL_WRITE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 2, struct _isfl *)
#define IOCTL_READ_MEMORY _IOWR(DEVICE_MAJOR_NUM, 3, struct _isfl *)
#define IOCTL_GENERATE_SMI _IOW(DEVICE_MAJOR_NUM, 4, struct _isfl *)
#define IOCTL_GET_SMI_STATUS _IOR(DEVICE_MAJOR_NUM, 5, struct _isfl *)
#define IOCTL_DISABLE_KBC _IOR(DEVICE_MAJOR_NUM, 6, struct _isfl *)
#define IOCTL_ENABLE_KBC _IOR(DEVICE_MAJOR_NUM, 7, struct _isfl *)
#define IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY _IOWR(DEVICE_MAJOR_NUM, 8, struct _isfl *)
#define IOCTL_FREE_MULTI_BLOCK_MEMORY _IOW(DEVICE_MAJOR_NUM, 9, struct _isfl *)
#define IOCTL_WRITE_TO_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 10, struct _mblock *)
#define IOCTL_READ_FROME_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 11, struct _mblock *)
#endif

558
x64/isfl_drv.c Normal file
View File

@ -0,0 +1,558 @@
/*
* InsydeFlash Linux Driver
* File: isfl_drv.c
*
* Copyright (C) 2005 - 2015 Insyde Software Corp.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/io.h>
typedef struct _PageAlloc {
unsigned long addr;
unsigned long PhyAddr;
unsigned int order;
}PageAlloc_t;
#include "isfl.h"
#define MAX_8042_LOOPS 100000
#define ISFL_VERSION "0.0.05"
#define DRIVER_AUTHOR "Howard Ho <howard.ho@insydesw.com.tw>"
#define DRIVER_DESC "InsydeFlash tool for Linux"
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
static dev_t first;
static struct cdev c_dev;
static struct class *isfl_class;
#endif
static int Device_Open = 0;
static unsigned char *isfl_mem_buff;
static unsigned char *g_ppbyMemoryBuffer[MAX_MEMORY_BLOCK_COUNT] = {0};
static PageAlloc_t g_ppbyMemoryBuffer2[MAX_MEMORY_BLOCK_COUNT] = {{0,0}};
static int isfl_open(struct inode *, struct file *);
static int isfl_release(struct inode *, struct file *);
static ssize_t isfl_read(struct file *, char *, size_t, loff_t *);
static ssize_t isfl_write(struct file *file, const char *buff, size_t length, loff_t *offset);
//Flash will close kbc except show dialog
static void enable_a20_kbc(void);
static void disable_a20_kbc(void);
//static int empty_8042(void);
static inline void io_delay(void);
//Use this function to get the message from AP
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
static long isfl_ioctl(struct file *file, unsigned int num, unsigned long arg)
#else
static int isfl_ioctl(struct inode *inode, struct file *file, unsigned int num, unsigned long arg)
#endif
{
unsigned long isfl_phys_addr; // Assgin to SW SMI
struct _isfl *isfl;
int isfl_size = sizeof( struct _isfl );
unsigned char buf[isfl_size];
struct _mblock *pBlock;
int iBlockStructureSize = sizeof( struct _mblock );
unsigned char *pbyBlockBuffer;
unsigned char *ch;
int i;
unsigned long mbuf_index;
unsigned char *vir_addr = 0;
switch ( num )
{
case IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY:
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
}
isfl = (struct _isfl *)buf;
mbuf_index = isfl->mbuf_paddr; // use mbuf_paddr to store mbuf_index to backward compatible
// Allocate memory buffer
g_ppbyMemoryBuffer2[mbuf_index].order = get_order( isfl->mbuf_size );
g_ppbyMemoryBuffer2[mbuf_index].addr = __get_free_pages( GFP_DMA32, g_ppbyMemoryBuffer2[mbuf_index].order );
g_ppbyMemoryBuffer[mbuf_index] = (unsigned char *)g_ppbyMemoryBuffer2[mbuf_index].addr;
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY allocate physical memory failed\n" );
return -1;
}
memset( g_ppbyMemoryBuffer[mbuf_index], 0, isfl->mbuf_size );
g_ppbyMemoryBuffer2[mbuf_index].PhyAddr = virt_to_phys( g_ppbyMemoryBuffer[mbuf_index] );
isfl->mbuf_paddr = g_ppbyMemoryBuffer2[mbuf_index].PhyAddr;
//printk( KERN_INFO "isfl alloc id=%d, PhyAddr=%x\n", mbuf_index, g_ppbyMemoryBuffer2[mbuf_index].PhyAddr );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
put_user( buf[i], ( ch + i ) );
}
break;
case IOCTL_FREE_MULTI_BLOCK_MEMORY:
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
}
isfl = (struct _isfl *)buf;
mbuf_index = -1;
isfl_phys_addr = isfl->mbuf_paddr;
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
mbuf_index = i;
if (-1 == mbuf_index)
return -1;
// Free Memory
if ( g_ppbyMemoryBuffer[mbuf_index] != NULL )
{
free_pages( g_ppbyMemoryBuffer2[mbuf_index].addr, g_ppbyMemoryBuffer2[mbuf_index].order );
g_ppbyMemoryBuffer2[mbuf_index].addr = 0;
g_ppbyMemoryBuffer2[mbuf_index].order = 0;
g_ppbyMemoryBuffer[mbuf_index] = NULL;
}
break;
case IOCTL_WRITE_TO_PHYSICAL:
ch = (unsigned char *)arg;
pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA );
if ( pbyBlockBuffer == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with NULL parameter\n" );
return -1;
}
for ( i = 0; i < iBlockStructureSize; i++ )
{
get_user( pbyBlockBuffer[i], ( ch + i ) );
}
pBlock = (struct _mblock *)pbyBlockBuffer;
isfl_phys_addr = pBlock->mbuf_PhyAddr;
//printk( KERN_INFO "isfl write PhyAddr=%x\n", pBlock->mbuf_PhyAddr);
//printk( KERN_INFO "isfl write len=%x\n", pBlock->mbuf_size);
//printk( KERN_INFO "isfl write PhyAddr=%x\n", isfl_phys_addr );
mbuf_index = -1;
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
mbuf_index = i;
if (-1 == mbuf_index)
{
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with wrong physcial address\n" );
return -1;
}
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with physcial address didn't allocate\n" );
return -1;
}
for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ )
{
g_ppbyMemoryBuffer[mbuf_index][i] = pBlock->pbyBuffer[i];
}
kfree( pbyBlockBuffer );
break;
case IOCTL_READ_FROME_PHYSICAL:
ch = (unsigned char *)arg;
pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA );
if ( pbyBlockBuffer == NULL )
{
return -1;
}
for ( i = 0; i < iBlockStructureSize; i++ )
{
get_user( pbyBlockBuffer[i], ( ch + i ) );
}
pBlock = (struct _mblock *)pbyBlockBuffer;
isfl_phys_addr = pBlock->mbuf_PhyAddr;
//printk( KERN_INFO "isfl read PhyAddr=%x\n", pBlock->mbuf_PhyAddr);
//printk( KERN_INFO "isfl read len=%x\n", pBlock->mbuf_size);
//printk( KERN_INFO "isfl read PhyAddr=%x\n", isfl_phys_addr );
mbuf_index = -1;
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
mbuf_index = i;
if (-1 == mbuf_index)
return -1;
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
{
return -1;
}
for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ )
{
pBlock->pbyBuffer[i] = g_ppbyMemoryBuffer[mbuf_index][i];
}
for ( i = 0; i < iBlockStructureSize; i++ )
{
put_user( pbyBlockBuffer[i], ( ch + i ) );
}
kfree( pbyBlockBuffer );
break;
case IOCTL_ALLOCATE_MEMORY:
//printk( KERN_INFO "IOCTL_ALLOCATE_MEMORY\n" );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
//printk( KERN_INFO "%02X ", buf[i] );
}
//printk( KERN_INFO "\n" );
isfl = (struct _isfl *)buf;
/* Allocate a memory buffer */
//printk( KERN_INFO "Allocate Memory size: %ld\n", isfl->mbuf_size );
isfl_mem_buff = kmalloc( isfl->mbuf_size, GFP_DMA ); //20090605 - Keep OS allocate under 16M
if ( isfl_mem_buff == NULL )
{
return -1;
}
memset( isfl_mem_buff, 0, isfl->mbuf_size );
isfl_phys_addr = virt_to_phys( isfl_mem_buff );
//printk( KERN_INFO "isfl: virt: 0x%p phys: 0x%08lx\n", isfl_mem_buff, isfl_phys_addr );
isfl->mbuf_paddr = isfl_phys_addr;
//printk( KERN_INFO "Memory physical addr: 0x%08lx\n", isfl->mbuf_paddr );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
//printk( KERN_INFO "%02X ", buf[i] );
put_user( buf[i], ( ch + i ) );
}
//printk( KERN_INFO "\n" );
break;
case IOCTL_FREE_MEMORY:
//printk( KERN_INFO "IOCTL_FREE_MEMORY\n" );
//kfree( isfl_mem_buff );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
//printk( KERN_INFO "%02X ", buf[i] );
}
isfl = (struct _isfl *)buf;
isfl_phys_addr = isfl->mbuf_paddr;
vir_addr = (unsigned char*)phys_to_virt (isfl_phys_addr);
if ( vir_addr == NULL )
{
//printk( KERN_INFO "isfl ioctl IOCTL_READ_FROME_PHYSICAL with failure from phys_to_virt\n" );
return -1;
}
kfree (vir_addr);
break;
case IOCTL_GENERATE_SMI:
//printk( "IOCTL_GENERATE_SMI\n" );
//printk( "size of (struct _isfl): %d\n", isfl_size );
/* Get SMI initial data */
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
get_user( buf[i], ( ch + i ) );
//printk( "%02X ", buf[i] );
}
//printk( "\n" );
isfl = (struct _isfl *)buf;
//printk( "SMI IO port: 0x%lX\n", isfl->smi_io );
//printk( "Bufer Size: %ld\n", isfl->mbuf_size );
break;
case IOCTL_GET_SMI_STATUS:
//printk( "IOCTL_GET_SMI_STATUS\n" );
ch = (unsigned char *)arg;
for ( i = 0; i < isfl_size; i++ )
{
//printk( "%02X ", buf[i] );
put_user( buf[i], ( ch + i ) );
}
//printk( "\n" );
break;
case IOCTL_ENABLE_KBC:
enable_a20_kbc();
break;
case IOCTL_DISABLE_KBC:
disable_a20_kbc();
break;
}
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
static DEFINE_MUTEX( isflash_mutex );
static long isfl_ioctl_unlock(struct file *fp, unsigned int cmd, unsigned long arg)
{
long ret;
mutex_lock( &isflash_mutex );
ret = isfl_ioctl( fp, cmd, arg );
mutex_unlock( &isflash_mutex );
return ret;
}
#endif
static struct file_operations fops = {
.read = isfl_read,
.write = isfl_write,
.open = isfl_open,
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
.unlocked_ioctl = isfl_ioctl_unlock,
.compat_ioctl = isfl_ioctl_unlock,
#else
.ioctl = isfl_ioctl,
#endif
.release = isfl_release
};
//Driver initialization
static int __init init_isfl(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
first = MKDEV(DEVICE_MAJOR_NUM, 0);
if (register_chrdev_region(first, 1, DEVICE_NAME) < 0)
{
return -1;
}
if ((isfl_class = class_create(THIS_MODULE, DEVICE_NAME)) == NULL)
{
unregister_chrdev_region(first, 1);
return -1;
}
if (device_create(isfl_class, NULL, first, NULL, "isfl") == NULL)
{
class_destroy(isfl_class);
unregister_chrdev_region(first, 1);
return -1;
}
cdev_init(&c_dev, &fops);
if (cdev_add(&c_dev, first, 1) == -1)
{
device_destroy(isfl_class, first);
class_destroy(isfl_class);
unregister_chrdev_region(first, 1);
return -1;
}
#else
int ret = register_chrdev(DEVICE_MAJOR_NUM, DEVICE_NAME, &fops);
if ( ret < 0 )
{
//printk( KERN_ALERT "Registering char device %s failed with %d\n", DEVICE_NAME, ret );
return ret;
}
#endif
//printk ("<0> IOCTL_ALLOCATE_MEMORY = %lx\n", IOCTL_ALLOCATE_MEMORY );
//printk ("<0> IOCTL_FREE_MEMORY = %lx\n", IOCTL_FREE_MEMORY );
//printk ("<0> IOCTL_WRITE_MEMORY = %lx\n", IOCTL_WRITE_MEMORY );
//printk ("<0> IOCTL_READ_MEMORY = %lx\n", IOCTL_READ_MEMORY );
//printk ("<0> IOCTL_GENERATE_SMI = %lx\n", IOCTL_GENERATE_SMI );
//printk ("<0> IOCTL_GET_SMI_STATUS = %lx\n", IOCTL_GET_SMI_STATUS );
//printk ("<0> IOCTL_DISABLE_KBC = %lx\n", IOCTL_DISABLE_KBC );
//printk ("<0> IOCTL_ENABLE_KBC = %lx\n", IOCTL_ENABLE_KBC );
//printk ("<0> IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY);
//printk ("<0> IOCTL_FREE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_FREE_MULTI_BLOCK_MEMORY );
//printk ("<0> IOCTL_COPY_BUFFER_TO_MEMORY = %lx\n", IOCTL_COPY_BUFFER_TO_MEMORY );
//printk ("<0> = %d\n", MAJOR(first));
return 0;
}
static void __exit cleanup_isfl(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
cdev_del(&c_dev);
device_destroy(isfl_class, first);
class_destroy(isfl_class);
unregister_chrdev_region(first, 1);
#else
unregister_chrdev( DEVICE_MAJOR_NUM, DEVICE_NAME );
#endif
}
static int isfl_open(struct inode *inode, struct file *file)
{
if ( Device_Open )
{
return -EBUSY;
}
Device_Open++;
try_module_get( THIS_MODULE );
return 0;
}
static int isfl_release(struct inode *inode, struct file *file)
{
Device_Open--;
module_put( THIS_MODULE );
return 0;
}
static ssize_t isfl_read(struct file *filp, char *buff, size_t len, loff_t *offset)
{
int bytes_read = 0;
//printk( KERN_INFO "isfl_read\n" );
if ( isfl_mem_buff == 0 )
{
//printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" );
return -EFAULT;
}
while ( len )
{
put_user( isfl_mem_buff[bytes_read++], buff++ );
len--;
}
//printk( KERN_INFO "isfl_read: read %d bytes, left %d\n", bytes_read, len );
return bytes_read;
}
static ssize_t isfl_write(struct file *file, const char *buff, size_t len, loff_t *offset)
{
int i;
//printk( KERN_INFO "isfl_write\n" );
if ( isfl_mem_buff == 0 )
{
//printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" );
return -EFAULT;
}
//printk( KERN_INFO "isfl_write: len: %d\n", len );
for ( i = 0; i < len && i < ISFB_FLASH_SIZE; i++ )
{
get_user( *( isfl_mem_buff + i ), buff + i );
}
#if 0
for ( i = 0; i < len; i++ )
{
if ( ( i + 1 ) % 8 )
{
//printk( "%02x ", isfl_mem_buff[i] );
}
else if ( ( i + 1 ) %16 )
{
//printk( "%02x ", isfl_mem_buff[i] );
}
else
{
//printk( "%02x\n", isfl_mem_buff[i] );
}
}
//printk( "\n" );
#endif
return i;
}
static int empty_8042(void)
{
u8 status;
int loops = MAX_8042_LOOPS;
while ( loops-- )
{
io_delay();
status = inb( 0x64 );
if ( status & 1 )
{
/* Read and discard input data */
io_delay();
(void)inb( 0x60 );
}
else if ( !( status & 2 ) )
{
/* Buffers empty, finished! */
return 0;
}
}
return -1;
}
static void enable_a20_kbc(void)
{
empty_8042();
outb( 0x60, 0x64 ); // Command write
empty_8042();
outb( 0x47, 0x60 ); // A20 on
empty_8042();
}
static void disable_a20_kbc(void)
{
empty_8042();
outb( 0x60, 0x64 ); // Command write
empty_8042();
outb( 0x74, 0x60 ); // A20 on
empty_8042();
}
static inline void io_delay(void)
{
const u16 DELAY_PORT = 0x80;
asm volatile( "outb %%al,%0" : : "dN" (DELAY_PORT) );
}
module_init( init_isfl );
module_exit( cleanup_isfl );
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_VERSION( ISFL_VERSION );
MODULE_SUPPORTED_DEVICE( "Insyde EFI BIOS Flash" );
MODULE_LICENSE( "GPL" );